(**************************************************************************)
(*                                                                        *)
(*  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 Dose_common
open Dose_debian
open Dose_doseparse

#define __label __FILE__
let label =  __label ;;
include Util.Logging(struct let label = label 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 Options = struct
  open OptParse

  let description = ("Given a buildgraph find a set of source packages which, if profile build, make the graph acyclic")
  let usage = "%prog buildgraph.xml Packages Soucres"

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

  let remove_weak = StdOpt.str_option ()
  let remove_reduced = str_list_option ()
  let cycle_length_fvs = StdOpt.int_option ~default:8 ()

  open OptParser ;;

  let prog_group = add_group options "Program specific options" in
  add options ~group:prog_group ~long_name:"max-length-fvs"
    ~help:"maximum length of found cycles for fvs search (default=4)" cycle_length_fvs;
  add options ~group:prog_group ~long_name:"remove-weak" ~help:"path to list of weak build dependencies" remove_weak;
  add options ~group:prog_group ~long_name:"remove-reduced" ~help:"remove droppable build dependencies supplied by comma separated list of reduced dep files" remove_reduced;

  include StdOptions.InputOptions;;
  let default = List.filter (fun e -> not (List.mem e ["compare"; "checkonly"; "latest";"outfile";"inputtype"])) 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-profiles"; "deb-ignore-essential"; "deb-builds-from"])) StdOptions.DistribOptions.default_options in
  StdOptions.DistribOptions.add_debian_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";
  StdDebug.all_quiet (OptParse.Opt.get Options.quiet);

  let maxlengthfvs = OptParse.Opt.get Options.cycle_length_fvs in
  let reduced_deps_files = OptParse.Opt.get Options.remove_reduced in
  let options = Options.set_deb_options () in
  let noindep = options.Debcudf.drop_bd_indep in
  let buildarch = Option.get options.Debcudf.native in
  let hostarch = match options.Debcudf.host with None -> "" | Some s -> s in
  let foreignarchs = options.Debcudf.foreign in

  let bgf, posargs = match posargs with
   | bgf::posargs -> bgf,posargs
   | _ -> fatal "require buildgraph.xml Packages Sources"
  in

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

  let tables = Debcudf.init_tables (fgsrclist@bgsrclist@binlist) in
  let fgsl = List.map (Debcudf.tocudf ?inst:None ~options tables) fgsrclist in
  let bgsl = List.map (Debcudf.tocudf ?inst:None ~options tables) bgsrclist in
  let pkglist = List.map (Debcudf.tocudf ?inst:None ~options tables) binlist in
  let universe = Cudf.load_universe (BootstrapCommon.unique [pkglist;fgsl;bgsl]) in

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

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

  (* retrieve all source packages in the build graph as cudf packages *)
  let srcpkglist = BGE.srcpkglist_of_g bg in

  (* set the file for weak dependencies *)
  let weak_file =
    if OptParse.Opt.is_set Options.remove_weak then
      OptParse.Opt.get Options.remove_weak
    else
      ""
  in

  (* create hashtable of reduced build dependencies *)
  let reduced_deps_ht,_ = BootstrapCommon.get_reduced_deps_ht ~weak_file (OptParse.Opt.is_set Options.remove_weak) (buildarch::foreignarchs) srcpkglist reduced_deps_files in

  (* test whether a feedback vertex set can be found *)
  let gt = BuildGraph.Utils.copy_graph bg in
  BGE.remove_build_deps reduced_deps_ht gt;
  
  if BuildGraph.Dfs.has_cycle gt then fatal "not enough droppable dependencies to make graph acyclic";

  let fvs = BGE.calculate_fvs ~maxlength:maxlengthfvs reduced_deps_ht bg in

  BuildGraph.VertexSet.iter (fun v ->
    Printf.printf "%s\n" (BGE.string_of_vertex ~noversion:true v)
  ) fvs;
;;

main ();;
