(**************************************************************************)
(*                                                                        *)
(*  Copyright (C) 2012 Johannes 'josch' Schauer <j.schauer@email.de>      *)
(*                                                                        *)
(*  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 DoseparseNoRpm

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

let str_list_option ?(default=Some []) ?(metavar = "STRLST") =
  let sep = "," in
  let coerce s = ExtString.String.nsplit s sep in
  fun () ->
    OptParse.Opt.value_option metavar default coerce
    (fun _ s -> Printf.sprintf "Invalid String '%s'" s)

module StringSet = BootstrapCommon.StringSet
module IntSet = BootstrapCommon.IntSet

module Options = struct
  open OptParse
  let usage = "%prog [options] --deb-native-arch=ARCH buildgraph.xml packages sources"
  let description = "given a buildgraph, output a feedback arc set"
  let options = OptParser.make ~description ~ usage
  include BootstrapCommon.MakeOptions(struct let options = options end)

  let cycle_length_fas = StdOpt.int_option ~default:8 ()
  let noindep = StdOpt.store_false ()

  open OptParser

  let prog_group = add_group options "Program specific options" in

  add options ~group:prog_group ~long_name:"max-length-fas" ~help:"maximum length of found cycles for fas search (default=4)" cycle_length_fas;
  add options ~group:prog_group ~long_name:"keep-indep" ~help:"Do not drop Build-Depends-Indep dependencies" noindep;

  include StdOptions.InputOptions;;
  let default = List.filter (fun e -> not (List.mem e ["checkonly"; "latest";"outfile"])) 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);
  Util.Debug.disable "Depsolver_int";
  Util.Warning.disable "Sources"; (* disable MismatchSrc warnings as exception is caught *)
  StdDebug.all_quiet (OptParse.Opt.get Options.quiet);
  let maxlengthfas = OptParse.Opt.get Options.cycle_length_fas in
  let noindep = OptParse.Opt.get Options.noindep in
  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 bgf, posargs = match posargs with
   | bgf::l -> bgf,l
   | _ -> fatal "you must provide buildgraph, Packages and Sources"
  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 pkglist = List.map (Debian.Debcudf.tocudf ~options tables) binlist in
  let universe = Cudf.load_universe (BootstrapCommon.unique [pkglist;fgsl;bgsl]) in

  let module BG = BuildGraph.G in

  let ic = open_in bgf in
  let bg = BuildGraph.from_ic universe ic in
  close_in ic;

  let module BGE = BuildGraphExtras.Make(struct let univ = universe end) in

  let ht_fas = Hashtbl.create 100 in
  BuildGraph.EdgeSet.iter (fun (src,_,pkg) ->
      Hashtbl.replace ht_fas src (pkg::(Hashtbl.find_default ht_fas src []))
    ) (BGE.calculate_fas ~maxlength:maxlengthfas bg);
  let fas = Hashtbl.fold (fun k v acc -> (k,v)::acc) ht_fas [] in
  List.iter (fun (k,v) ->
      Printf.printf "%s %s\n"
        (BGE.string_of_vertex k)
        (String.concat " " (List.map BGE.string_of_vertex v));
    ) fas;
;;

main ();;
