(**************************************************************************)
(*                                                                        *)
(*  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 binary packages, return the corresponding source packages"
  )
  let usage = "%prog Packages... Sources"

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

  let addarchall = StdOpt.store_true ()
  let noindep = StdOpt.store_false ()
  let allowsrcmismatch = StdOpt.store_true ()

  open OptParser

  let prog_group = add_group options "Program specific options" in

  add options ~group:prog_group ~long_name:"all" ~help:"also add source packages for Architecture:all packages" addarchall;
  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;

  include StdOptions.InputOptions;;
  let default = List.filter (fun e -> not (List.mem e ["checkonly";"latest";"fg";"bg"])) 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);

  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 addarchall = OptParse.Opt.get Options.addarchall in
  let noindep = OptParse.Opt.get Options.noindep in
  let allowsrcmismatch = OptParse.Opt.get Options.allowsrcmismatch in

  let binlist, (fgsrclist, bgsrclist), origsrclist = BootstrapCommon.parse_packages ~noindep Options.parse_cmdline buildarch hostarch foreignarchs posargs in
  let srclist = fgsrclist @ bgsrclist in
  let tables = Debian.Debcudf.init_tables (srclist@binlist) in
  let sl = List.map (Debian.Debcudf.tocudf ~options tables) srclist in
  let bl = List.map (Debian.Debcudf.tocudf ~options tables) binlist in

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

  let bl =
    if addarchall then
      bl
    else
      List.filter BootstrapCommon.pkg_is_not_arch_all bl
  in

  (* create a hashtable mapping package name and version tuples to
   * Sources.source format822 stanzas *)
  let cudftosrc_table = Hashtbl.create 30000 in
  List.iter2 (fun cudfpkg -> fun srcpkg ->
    let id = (cudfpkg.Cudf.package, cudfpkg.Cudf.version) in
    Hashtbl.add cudftosrc_table id srcpkg
  ) sl origsrclist;


  let universe = Cudf.load_universe (bl@sl) in

  (* create the set of cudf source packages from which the input binary
   * packages are built *)
  let srcs = List.fold_left (fun acc bin ->
    let src = try BootstrapCommon.get_src_package ~allowmismatch:allowsrcmismatch universe bin
    with Sources.NotfoundSrc -> failwith (Printf.sprintf "cannot find source for binary package %s" (CudfAdd.string_of_package bin)) in 
    CudfAdd.Cudf_set.add src acc
  ) CudfAdd.Cudf_set.empty bl in

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

  (* for each of those source packages, get the associated format822 stanza and
   * print it *)
  List.iter (fun cudfpkg ->
    let id = (cudfpkg.Cudf.package,cudfpkg.Cudf.version) in
    let s = Hashtbl.find cudftosrc_table id in
    Debian.Printer.pp_source oc s;
    output_char oc '\n';
  ) (BootstrapCommon.pkg_sort (CudfAdd.Cudf_set.elements srcs));

  close_out oc;
;;

main ();;
