(*
This file is part of Ceve.

Ceve is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

Ceve is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Ceve; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)

(**
Module to output data in CNF format (for either tart or a SAT solver
@author Jaap Boender
*)

open Napkin

let output_tart_dependencies (ht: Dependencies.pkg_hashtable) (pkg: default_package): unit =
let var_name (n: string) (v: string) (a: string): string =
begin
	n ^ "'" ^ v ^ "@" ^ a
end in
begin
	let dep_str = Tools.uniq (List.sort compare
	(List.map (fun disj ->
	let deps = List.flatten (List.map (fun d -> 
	match d with
	| Unit_version (target, spec) ->
		List.map
			(fun (v, m) -> var_name target v m.pk_architecture)
		(List.filter
			(fun (v, m) -> Dependencies.can_provide (Sel_EQ v) spec)
		(Hashtbl.find_all ht target))
	| Glob_pattern _ -> raise (Failure "ARGH V"))
	disj) in
		if deps <> [] then
			"!" ^ (var_name pkg.pk_unit pkg.pk_version pkg.pk_architecture) ^ " " ^ (String.concat " " (Tools.uniq (List.sort compare deps))) ^ "\n"
		else
			""
	) (pkg.pk_depends @ pkg.pk_pre_depends))) in
	let cfl_str = Tools.uniq (List.sort compare (List.flatten (List.map (fun c ->
	match c with
	| Unit_version (cfl_t, cfl_s) ->
		(List.map
			(fun (v, m) -> "!" ^ (var_name pkg.pk_unit pkg.pk_version pkg.pk_architecture) ^ " !" ^ (var_name cfl_t v m.pk_architecture))
		(List.filter
			(fun (v, m) -> Dependencies.can_provide (Sel_EQ v) cfl_s && ((m.pk_unit <> pkg.pk_unit) || (m.pk_version <> pkg.pk_version)))
		(Hashtbl.find_all ht cfl_t)))
	| Glob_pattern _ -> raise (Failure "ARGH VI")
	) pkg.pk_conflicts))) in
		if ((dep_str = []) || (dep_str = [""])) && (cfl_str = []) then
			output_string !Options.output_channel ((var_name pkg.pk_unit pkg.pk_version pkg.pk_architecture) ^ " !" ^ (var_name pkg.pk_unit pkg.pk_version pkg.pk_architecture) ^ "\n")
		else
		begin
			if dep_str <> [] then
				output_string !Options.output_channel (String.concat "" dep_str);
			if cfl_str <> [] then
				output_string !Options.output_channel ((String.concat "\n" cfl_str) ^ "\n")
		end
end;;

let output_tart_cnf (pkgs: default_package list): unit =
let ht = Dependencies.create_pkgs_hashtable pkgs in
begin
	prerr_endline "[CNF] Outputting Tart CNF lines...";
	List.iter (output_tart_dependencies ht) pkgs
end;;

let output_dimacs_cnf (pkgs: default_package list): unit =
let ht = Dependencies.create_pkgs_hashtable pkgs in
let vars = Hashtbl.create (List.length pkgs) in
let clauses = ref [] in
let get_variable pkg = try
  Hashtbl.find vars (pkg.pk_unit, pkg.pk_version, pkg.pk_architecture)
with Not_found ->
begin
  let n = Hashtbl.length vars + 1 in
  Hashtbl.add vars (pkg.pk_unit, pkg.pk_version, pkg.pk_architecture) n;
  n
end in
begin
  List.iter (fun pkg ->
    let p_var = get_variable pkg in
    List.iter (fun disj ->
      let dcl = -p_var::(List.map (fun (_, m) -> get_variable m) (List.flatten (List.map (function
      | Unit_version (target, spec) ->
        List.filter (fun (v, m) -> Dependencies.can_provide (Sel_EQ v) spec && pkg.pk_unit <> m.pk_unit) 
          (Hashtbl.find_all ht target)
      | Glob_pattern _ -> raise (Failure "ARGH VII")
      ) disj))) in
        clauses := dcl::!clauses;
    ) (pkg.pk_depends @ pkg.pk_pre_depends);
    List.iter (function
    | Unit_version (target, spec) ->
        List.iter (fun (_, m) ->
          clauses := [-p_var; -(get_variable m)]::!clauses
        )
        (List.filter (fun (v, m) -> Dependencies.can_provide (Sel_EQ v) spec && 
        ((m.pk_unit <> pkg.pk_unit) || (m.pk_version <> pkg.pk_version))) (Hashtbl.find_all ht target)) 
    | Glob_pattern _ -> raise (Failure "ARGH VIII")
    ) pkg.pk_conflicts
  ) pkgs;
  let oc = !Options.output_channel in
  Printf.fprintf oc "p cnf %d %d\n" (Hashtbl.length vars) (List.length !clauses);
  List.iter (fun c ->
    Printf.fprintf oc "%s 0\n" (String.concat " " (List.map string_of_int c))
  ) !clauses
end;;
