/*==============================================================================
 *	LPAD and CP-Logic reasoning suite
 *	File: parsing.pl
 *	Parses predicates to load LPADs (main predicate: parse(FileNameNoExt)
 *	Copyright (c) 2009, Stefano Bragaglia
 *============================================================================*/
  
:- dynamic rule/4, def_rule/2.

:- use_module(params).
:- use_module(utility).

% :- source.
% :- yap_flag(single_var_warnings, on).





/* parse(File) 
 * -----------
 * This predicate parses the given .cpl file. 
 *
 * Note: it can be called more than once without exiting yap 
 *
 * INPUT
 *  - File: .cpl file to parse, without extension.
 */
parse(File) :- 
	atom_concat(File, '.cpl', FileName),
	open(FileName, read, FileHandle), 
	read_clauses(FileHandle, Clauses), 
	close(FileHandle), 
	retractall(rule_by_num(_, _, _, _, _)), 
	retractall(rule(_, _, _, _, _, _, _, _)), 
	retractall(def_rule(_, _)), 
	process_clauses(Clauses, 1).







/* assert_rules()
 * --------------
 * This tail recursive predicate parses the given list of (Head:Prob) couples
 * and stores them incrementally as rules along with the other parameters.
 *
 * INPUT
 *  - Head: current head part.
 *  - Prob: probability of the current head part.
 *  - Index: index of the current head part.
 *  - Subst: substitution for the current head part.
 *  - Choices: list of current head parts indexes.
 *  - HeadList: complete head or list of its parts.
 *  - BodyList: complete body or list of its parts.
 */
assert_rules([], _Index, _HeadList, _BodyList, _Choices, _Id, _Subst) :- !. % Closing condition.

assert_rules(['':_Prob], _Index, _HeadList, _BodyList, _Choices, _Id, _Subst) :- !.

assert_rules([Head:Prob|Tail], Index, HeadList, BodyList, Choices, Id, Subst) :- 
	assertz(rule(Head, Prob, Index, Id, Subst, Choices, HeadList, BodyList)), 
	Next is Index + 1, 
	assert_rules(Tail, Next, Id, Subst, Choices, HeadList, BodyList).



delete_var(_Var, [], []).

delete_var(Var, [Current|Tail], [Current|Next]) :- 
	Var \== Current, !, 
	delete_var(Var, Tail, Next).

delete_var(_Var, [_Head|Tail], Tail).



extract_vars(Variable, Var0, Var1) :- 
	var(Variable), !, 
	(member_eq(Variable, Var0) ->
		Var1 = Var0;
		append(Var0, [Variable], Var1)).

extract_vars(Term, Var0, Var1) :- 
	Term=..[_F|Args], 
	extract_vars_list(Args, Var0, Var1).


 
extract_vars_clause(end_of_file, []).

extract_vars_clause(Clause, VarNames, Couples) :- 
	(Clause = (Head :- _Body) ->
		true;
		Head = Clause), 
	extract_vars(Head, [], Vars), 
	pair(VarNames, Vars, Couples).
	


extract_vars_list([], Var, Var).

extract_vars_list([Term|Tail], Var0, Var1) :- 
	extract_vars(Term, Var0, Var), 
	extract_vars_list(Tail, Var, Var1).



get_var(Var, [Var]) :- 
	var(Var), !. % Succeeds if Var is currently a free variable, otherwise fails. 

get_var(Var, Value) :- 
	Var=..[_F|Args],
	get_var_list(Args, Value).



get_var_list([], []).

get_var_list([Head|Tail], [Head|Next]) :- 
	var(Head), !, 
	get_var_list(Tail, Next).

get_var_list([Head|Tail], Vars) :- !, 
	get_var(Head, Var), 
	append(Var, Next, Vars), 
	get_var_list(Tail, Next).



/* ground_prob(HeadList)
 * ---------------------
 * This tail recursive predicate verifies if the given HeadList is ground.
 *
 * INPUT
 *  - HeadList: list of heads to verify its groundness.
 */
ground_prob([]).

ground_prob([_Head:ProbHead|Tail]) :- 
	ground(ProbHead), % Succeeds if there are no free variables in the term ProbHead.
	ground_prob(Tail).



pair(_VarName, [], []).

pair([VarName = _Var|TailVarName], [Var|TailVar], [VarName = Var|Tail]) :- 
	pair(TailVarName, TailVar, Tail).	



/* process_head(HeadList, CompleteHeadList)
 * ----------------------------------------
 * Note: if the annotation in the head are not ground, the null atom is not 
 *       added and the eventual formulas are not evaluated.
 */	
process_head(HeadList, GroundHeadList) :- 
	ground_prob(HeadList), !,
	process_head_ground(HeadList, 0, GroundHeadList);
	 
process_head(HeadList, HeadList).



/* process_head_ground([Head:ProbHead], Prob, [Head:ProbHead|Null])
 * ----------------------------------------------------------------
 */
process_head_ground([Head:ProbHead], Prob, [Head:ProbHead|Null]) :- 
	ProbLast is 1 - Prob - ProbHead, 
	setting(epsilon_parsing, Eps), 
	EpsNeg is - Eps, 
	ProbLast > EpsNeg, 
	(ProbLast > Eps ->
		Null = ['':ProbLast];
		Null = []). 

process_head_ground([Head:ProbHead|Tail], Prob, [Head:ProbHead|Next]) :- 
	ProbNext is Prob + ProbHead, 
	process_head_ground(Tail, ProbNext, Next).

















/* process_body(BodyList, Vars0, Vars1)
 * ------------------------------------
 * Note: setof must have a goal in the form B^G, where B is a term containing 
 *       the existential variables.
 */
process_body([], Vars, Vars).

process_body([setof(A, B^_G, _L)|Tail], Vars0, Vars1) :- !, 
	get_var(A, VarsA), 
	get_var(B, VarsB), 
	remove_vars(VarsA, Vars0, Vars3), 
	remove_vars(VarsB, Vars3, Vars2), 
	process_body(Tail, Vars2, Vars1).

process_body([setof(A, _G, _L)|Tail], Vars0, Vars1) :- !, 
	get_var(A, VarsA), 
	remove_vars(VarsA, Vars0, Vars2), 
	process_body(Tail, Vars2, Vars1).

process_body([bagof(A, B^_G, _L)|Tail], Vars0, Vars1) :- !, 
	get_var(A, VarsA), 
	get_var(B, VarsB), 
	remove_vars(VarsA, Vars0, Vars3), 
	remove_vars(VarsB, Vars3, Vars2), 
	process_body(Tail, Vars2, Vars1).

process_body([bagof(A, _G, _L)|Tail], Vars0, Vars1) :- !, 
	get_var(A, VarsA), 
	remove_vars(VarsA, Vars0, Vars2), 
	process_body(Tail, Vars2, Vars1).

process_body([_Head|Tail], Vars0, Vars1) :- !, 
	process_body(Tail, Vars0, Vars1).







process_clauses([(end_of_file, [])], _Id).

/* NB: il seguente predicato è stato commentato perchè usa predicati non conformi
 *     a quelli attesi (vedi 'rule\5').
 * /
process_clauses([((Head :- Body), Value)|Tail], Id) :- 
	Head=uniform(A, P, L), !, 
	list2and(BodyList, Body), 
	process_body(BodyList, Value, BodyListValue), 
	remove_vars([P], BodyListValue, V2), 
	append(BodyList, [length(L, Tot), nth0(Number, L, P)], BL1), 
	append(V2, ['Tot'=Tot], V3), 
	assertz(rule(Id, V3, _NH, uniform(A:1/Tot, L, Number), BL1)), 
	assertz(rule_uniform(A, Id, V3, _NH, 1/Tot, L, Number, BL1)), 
	N1 is Id+1, 
	process_clauses(Tail, N1). */

process_clauses([((Head :- Body), Value)|Tail], Id) :- 
	Head = (_;_), !, 
	list2or(HeadListOr, Head), 
	process_head(HeadListOr, HeadList), 
	list2and(BodyList, Body), 
	process_body(BodyList, Value, BodyListValue), 
	length(HeadList, LH), 
	listN(0, LH, NH), 
	assert_rules(HeadList, 0, HeadList, BodyList, NH, Id, BodyListValue), 
	assertz(rule_by_num(Id, BodyListValue, NH, HeadList, BodyList)), 
	N1 is Id+1, 
	process_clauses(Tail, N1).

process_clauses([((Head :- Body), Value)|Tail], Id) :- 
	Head = (_:_), !, 
	list2or(HeadListOr, Head), 
	process_head(HeadListOr, HeadList), 
	list2and(BodyList, Body), 
	process_body(BodyList, Value, BodyListValue), 
	length(HeadList, LH), 
	listN(0, LH, NH), 
	assert_rules(HeadList, 0, HeadList, BodyList, NH, Id, BodyListValue), 
	assertz(rule_by_num(Id, BodyListValue, NH, HeadList, BodyList)), 
	N1 is Id+1, 
	process_clauses(Tail, N1).
	
process_clauses([((Head :- Body), _V)|Tail], Id) :- !, 
	list2and(BodyList, Body), 
	assert(def_rule(Head, BodyList)), 
	process_clauses(Tail, Id).

process_clauses([(Head, Value)|Tail], Id) :- 
	Head=(_;_), !, 
	list2or(HeadListOr, Head), 
	process_head(HeadListOr, HeadList), 
	length(HeadList, LH), 
	listN(0, LH, NH), 
	assert_rules(HeadList, 0, HeadList, [], NH, Id, Value), 
	assertz(rule_by_num(Id, Value, NH, HeadList, [])), 
	N1 is Id+1, 
	process_clauses(Tail, N1).

process_clauses([(Head, Value)|Tail], Id) :- 
	Head=(_:_), !, 
	list2or(HeadListOr, Head), 
	process_head(HeadListOr, HeadList), 
	length(HeadList, LH), 
	listN(0, LH, NH), 
	assert_rules(HeadList, 0, HeadList, [], NH, Id, Value), 
	assertz(rule_by_num(Id, Value, NH, HeadList, [])), 
	N1 is Id+1, 
	process_clauses(Tail, N1).
	
process_clauses([(Head, _V)|Tail], Id) :- 
	assert(def_rule(Head, [])), 
	process_clauses(Tail, Id).



read_clauses(Stream, Clauses) :- 
	(setting(ground_body, true) ->
		read_clauses_ground_body(Stream, Clauses);
		read_clauses_exist_body(Stream, Clauses)).



read_clauses_exist_body(Stream, [(Clause, Vars)|Next]) :- 
	read_term(Stream, Clause, [variable_names(VarNames)]), 
	extract_vars_clause(Clause, VarNames, Vars), 
	(Clause = end_of_file ->
		Next = [];
		read_clauses_exist_body(Stream, Next)).



read_clauses_ground_body(Stream, [(Clause, Vars)|Next]) :- 
	read_term(Stream, Clause, [variable_names(Vars)]), 
	(Clause = end_of_file ->
		Next = [];
		read_clauses_ground_body(Stream, Next)).



remove_vars([], Vars, Vars).

remove_vars([Head|Tail], Vars0, Vars1) :- 
	delete_var(Head, Vars0, Vars2), 
	remove_vars(Tail, Vars2, Vars1).
