(**************************************************************************)
(*                                                                        *)
(*  Copyright (C) 2012 Johannes 'josch' Schauer <j.schauer@email.de>      *)
(*  Copyright (C) 2012 Pietro Abate <pietro.abate@pps.jussieu.fr>         *)
(*                                                                        *)
(*  This library is free software: you can redistribute it and/or modify  *)
(*  it under the terms of the GNU Lesser General Public License as        *)
(*  published by the Free Software Foundation, either version 3 of the    *)
(*  License, or (at your option) any later version.  A special linking    *)
(*  exception to the GNU Lesser General Public License applies to this    *)
(*  library, see the COPYING file for more information.                   *)
(**************************************************************************)

open ExtLib
open Common
open Debian
open DoseparseNoRpm

include Util.Logging(struct let label = __FILE__ end) ;;

module Options = struct
  open OptParse
  let description = (
    "given a list of source packages, return the binary packages those source packages build"
  )
  let usage = "%prog [options] Packages... Sources"

  let options = OptParser.make ~description ~usage
  include BootstrapCommon.MakeOptions(struct let options = options end)

  let noindep = StdOpt.store_false ()
  let allowsrcmismatch = StdOpt.store_true ()
  let ignoresrclessbin = StdOpt.store_true ()
  let available = StdOpt.str_option ()

  open OptParser

  let prog_group = add_group options "Program specific options" in

  add options ~group:prog_group ~short_name:'A' ~long_name:"available" ~help:"List of available packages (arch:all, crossed...) in control file format" available;
  add options ~group:prog_group ~long_name:"keep-indep" ~help:"Do not drop Build-Depends-Indep dependencies" noindep;
  add options ~group:prog_group ~long_name:"allowsrcmismatch" ~help:("If a binary package is "^
    "without a source package but there is a source package of same name but "^ 
    "different version, match this binary package to that source package.") allowsrcmismatch;
  add options ~group:prog_group ~long_name:"ignoresrclessbin" ~help:("Ignore binary packages that "^
    "do not have an associated source package. If --allowsrcmismatch is supplied, then this rule "^
    "is applied after it.") ignoresrclessbin;

  include StdOptions.InputOptions;;
  let default = List.filter (fun e -> not (List.mem e ["checkonly";"latest"])) StdOptions.InputOptions.default_options in
  StdOptions.InputOptions.add_options ~default options;;

  include StdOptions.DistribOptions;;
  let default = List.filter (fun e -> not (List.mem e ["deb-ignore-essential"; "inputtype"])) StdOptions.DistribOptions.default_options in
  StdOptions.DistribOptions.add_options ~default options;;
end

let main () =
  let posargs = OptParse.OptParser.parse_argv Options.options in
  StdDebug.enable_debug (OptParse.Opt.get Options.verbose);
  StdDebug.all_quiet (OptParse.Opt.get Options.quiet);
  Util.Warning.disable "Sources";

  let options = Options.set_deb_options () in
  let buildarch = Option.get options.Debian.Debcudf.native in
  let hostarch = match options.Debian.Debcudf.host with None -> "" | Some s -> s in
  let foreignarchs = options.Debian.Debcudf.foreign in
  let noindep = OptParse.Opt.get Options.noindep in
  let allowsrcmismatch = OptParse.Opt.get Options.allowsrcmismatch in
  let ignoresrclessbin = OptParse.Opt.get Options.ignoresrclessbin in

  let binlist, (fgsrclist,bgsrclist), _ = BootstrapCommon.parse_packages ~noindep Options.parse_cmdline buildarch hostarch foreignarchs posargs in

  let tables = Debian.Debcudf.init_tables (fgsrclist@bgsrclist@binlist) in
  let fgsl = List.map (Debian.Debcudf.tocudf ~options tables) fgsrclist in
  let bgsl = List.map (Debian.Debcudf.tocudf ~options tables) bgsrclist in
  let bl = List.map (Debian.Debcudf.tocudf ~options tables) binlist in

  (* create a hashtable mapping cudf package name,version,arch tuples to
   * Packages.package format822 stanzas *)
  let cudftobin_table = Hashtbl.create 30000 in
  List.iter2 (fun cudfpkg -> fun binpkg ->
    let arch =
      try Some (Cudf.lookup_package_property cudfpkg "architecture")
      with Not_found -> None
    in
    let id = (cudfpkg.Cudf.package, cudfpkg.Cudf.version, arch) in
    Hashtbl.add cudftobin_table id binpkg
  ) bl binlist;

  let bl = if OptParse.Opt.get Options.latest then CudfAdd.latest bl else bl in

  let universe = Cudf.load_universe (BootstrapCommon.unique [fgsl;bgsl;bl]) in

  (* read package list for available packages *)
  let availableset =
    if OptParse.Opt.is_set Options.available then
      BootstrapCommon.read_package_file ~archs:(buildarch::hostarch::foreignarchs) (Debcudf.tocudf ~options tables) (OptParse.Opt.get Options.available)
    else CudfAdd.Cudf_set.empty
  in

  let bin = Sources.binset (BootstrapCommon.get_bin_packages (BootstrapCommon.srcbin_table ~available:availableset ~allowmismatch:allowsrcmismatch ~ignoresrclessbin universe)) fgsl in

  let oc =
    if OptParse.Opt.is_set Options.outfile then
      open_out (OptParse.Opt.get Options.outfile)
    else
      stdout
  in

  (* for each binary package, get the associated format822 stanza and print it
   * to stdout *)
  List.iter (fun p ->
    let arch =
      try Some (Cudf.lookup_package_property p "architecture")
      with Not_found -> None
    in
    let id = (p.Cudf.package, p.Cudf.version, arch) in
    let b = Hashtbl.find cudftobin_table id in
    Debian.Printer.pp_package oc b;
    output_char oc '\n';
  ) (BootstrapCommon.pkg_sort (CudfAdd.Cudf_set.elements bin));

  close_out oc;
;;

main ();;
