%
% Splitting a fused CSP into multiple DBQs and a sub-CSP.
%
% We look for UQCs with restrict_subquery/3 which do not have any
% extra filtering condition in the generator.
%

:- module(split_csp,	[	decompose_csp/4,
				split_csp/4,
				print_dbq/1
			]).

:- use_module('../../prolog/util/prolog_detect').
:- prolog_detect:prolog(Prolog),
	(	(	Prolog=sicstus,
			use_module(library(lists))
		);
		true
	).

:- use_module('../../prolog/util/cif_util').
:- use_module('../../prolog/util/print_cif').

%
% dump a list of DBQs
%
print_dbq([]) :-
	!.

print_dbq([D|Ds]) :-
	print_dbq(D),
	print_dbq(Ds).

print_dbq(con(Var,Type,Cif)) :-
	format("~q:~q:",[Var,Type]),
	print_cif(Cif),
	nl.

print_dbq(dbq(Vars,Zf)) :-
	write(Vars),
	nl,
	print_cif(Zf),
	nl.

%
% decompose a CSP into DBQ & sub-CSP
% filtering empty DBQ
%
decompose_csp(multiple,Cif,Csp,Dbq) :-
	split_csp(multiple,Cif,Csp,Dbq_with_type),
	filter_empty_dbq(Dbq_with_type,Dbq).

%
% filter out meaningless or empty queries
%
filter_empty_dbq([],[]).
filter_empty_dbq([Q|Qs],Filtered) :-
	Q=dbq(Vars:Types,Cif),
	!,
	cif_util:filter_basic_types_generate(Cif,F),
	(	(	F=[],
			!,
			filter_empty_dbq(Qs,Filtered)
		);
		filter_empty_dbq(Qs,Fs),
		Filtered=[dbq(Vars:Types,F)|Fs]
	).
/*
filter_basic_types_generate([],[]).
filter_basic_types_generate([],[]).

filter_basic_types_generate([],[]).
filter_basic_types_generate([C|Cs],Fs) :-
	(	C=generate(integer,_);
		C=generate(string,_);
		C=generate(boolean,_);
		C=generate(float,_)
	),
	!,
	filter_basic_types_generate(Cs,Fs).
filter_basic_types_generate([C|Cs],[C|Fs]) :-
	filter_basic_types_generate(Cs,Fs).
*/

%
% split_csp/4
%
% split a CSP into DB queries and a reduced CSP
%
% We collect constraint that surely apply on variables
% and ignore those conditional one which may not apply
% In collecting constraint information, we ignore location information
% of entity classes. Instead, we visualise a virtual database
% described by the integration schema where all entities can
% be access through a database mediator.
%
% split_csp(+Mode,+Cif,-Csp,-Dbq)
%
% Mode	=	single/multiple
%		currently only 'multiple' is supported as 'single' doesn't make much sense
% Cif	=	the CIF to be splitted
% Csp	=	the reduced CSP
% DBQ	=	the list of DBQs composed
%

split_csp(multiple,Cif,Csp,Dbq_with_type) :-
	find_var_types(Cif,Types),						%find all vars w/ type
	findall(Template,(	member(Var:Type,Types),				%make up the default DBQ
				Template=dbq([Var],[generate(Type,Var)])
			),
		Starting_dbq),
	split_csp(multiple,Cif,Csp,Starting_dbq,Ending_dbq),			%proceed to extract more info.
	put_type_into_dbq(Ending_dbq,Types,Dbq_with_type).			%put type info. into DBQ so that it follows CIF



%
% split_csp/5, cater for a list
%
split_csp(_,[],[],Dbq,Dbq) :-					%end of list, no change to DBQ
	!.

split_csp(Mode,[C|Cs],Csp,Old_dbq,New_dbq) :-
	!,
	split_csp(Mode,C,C_csp,Old_dbq,Temp_dbq),		%work on the head, getting some DBQs
	split_csp(Mode,Cs,Cs_csp,Temp_dbq,New_dbq),		%then the tail, getting more DBQs (monotonically)
	append_cif(C_csp,Cs_csp,Csp).				%join the CSPs together, DBQ should be updated already



%:::::::::::::::::::::::::
% different CIF constructs
%:::::::::::::::::::::::::

%
% group/2, just proceed w/ the CIF
%
split_csp(Mode,group(_,Cif),Csp,Old_dbq,New_dbq) :-
	!,
	split_csp(Mode,Cif,Csp,Old_dbq,New_dbq).	%if it is s group/2, just work on the CIF

%
% restrict_subquery/3 applying to ALL
% i.e. without any filter in G
%
split_csp(multiple,C,Csp,Old_dbq,New_dbq) :-
	C=restrict_subquery(all(Vs),G,P),
	all_can_unify(G,[	generate(_,_),
				generate(_,_,_,_,_),
				expression(_,_,relative(_,_,_)),
				relative(_,_,_)
			]),					%G consists of generate/2 & generate/5 only
	!,
	split_csp(multiple,G,Reduced_g,Old_dbq,Temp_dbq),
	split_csp(multiple,P,Reduced_p,Temp_dbq,Temp_dbq2),

	append_cif(Reduced_g,Reduced_p,Zf),
%	combine_dbq(Old_dbq,dbq(Vs,Zf),New_dbq),
	combine_dbq(Temp_dbq2,dbq(Vs,Zf),New_dbq),
	Csp=[].

%
% restrict_subquery/3 with filters in G
% recursively look into G & P
%
split_csp(multiple,C,Csp,Old_dbq,New_dbq) :-
	C=restrict_subquery(Q,G,P),
	!,
	split_csp(multiple,G,Reduced_g,Old_dbq,Temp_dbq),
	split_csp(multiple,P,Reduced_p,Temp_dbq,New_dbq),
	Csp=restrict_subquery(Q,Reduced_g,Reduced_p).		%*** MAY NOT WORK


%
% explicit set definition
%
split_csp(multiple,C,Csp,Old_dbq,New_dbq) :-
	C=generate_subquery(Type,Var,explicit(Set),_,_),		%and explicit set
	!,
	Zf=explicit(Set),
	combine_dbq(Old_dbq,dbq([Var],Zf),New_dbq),
	Csp=generate(Type,Var).

%
% int_set
%
split_csp(multiple,C,Csp,Old_dbq,New_dbq) :-
	C=int_set(_,_,Var),
	!,
	combine_dbq(Old_dbq,dbq([Var],C),New_dbq),
	Csp=generate(integer,Var).

%
% a singleton set
%
split_csp(multiple,C,Csp,Old_dbq,New_dbq) :-
	C=generate_subquery(Type,Var,the(Icode),_,_),
	combine_dbq(Old_dbq,dbq([Var],the(Icode)),New_dbq),
	Csp=generate(Type,Var).


%
% all others, no extraction
%
split_csp(multiple,C,C,Old_dbq,Old_dbq) :-
	!.






/*

%
% split_can_unify/4
%
% split a CIF into 2 parts
% 1 contains constructs which can unify w/ Terms
% and the other one cannot
%
split_can_unify([],_,[],[]).

split_can_unify([C|Cs],Term,[C|Can],Cannot) :-
	can_unify(C,Term),
	!,
	split_can_unify(Cs,Term,Can,Cannot).

split_can_unify([C|Cs],Terms,[C|Can],Cannot) :-
	member(M,Terms),
	can_unify(C,M),
	!,
	split_can_unify(Cs,Terms,Can,Cannot).

split_can_unify([C|Cs],Terms,Can,[C|Cannot]) :-
	split_can_unify(Cs,Terms,Can,Cannot).
*/


%
% all_can_unify/2
%
% see if the whole list can unify w/ a certain structure
%
% all_can_unify(Cif,Term)
%
% Cif	=	the list of Cif to test
% Term	=	If it is a term, try to see if all members in
%		Cif can unify with Term
%		If it is a list, try to see if all members in
%		Cif can unify with a member of Term
%
all_can_unify([],_).			%end of list

all_can_unify([C|Cs],Term) :-
	can_unify(C,Term),		%C can unify with Term
	!,
	all_can_unify(Cs,Term).		%proceed with the rest

all_can_unify([C|Cs],List) :-
	is_list(List),			%test against a list of terms
	member(Term,List),		%there is a member in List
	can_unify(C,Term),		%which can unify w/ C
	all_can_unify(Cs,List).		%proceed w/ the rest

%
% exist_can_unify/2
%
% see if there exist something in the list
% which can unify w/ a certain structure
%
% exist_can_unify(Cif,Term)
%
% Cif	=	the list of CIF to test
% Term	=	If it is a term, try to see if there is a member in
%		Cif such that it can unify with Term
%		If it is a term, try to see if there exists a member
%		in Cif which can unify with a member in Term
%
exist_can_unify([C|_],Term) :-
	can_unify(C,Term),
	!.

exist_can_unify([C|_],List) :-
	is_list(List),
	member(Term,List),
	can_unify(C,Term),
	!.

exist_can_unify([_|Cs],Term) :-
	exist_can_unify(Cs,Term).


%
% can_unify/2
%
% test unifiability of 2 terms but not unifying them
%
can_unify(A,B) :-
	copy_term(A,A_clone),	%make a copy of A
	copy_term(B,B_clone),	%make a copy of A
	A_clone=B_clone.	%unifying the copy with B


%
% combine 2 multiple-class DBQs into 1
%
combine_dbq([],Dbq,[Dbq]) :-
	!.

combine_dbq(Old_dbq,dbq(Vars,Zf),New_dbq) :-
	An_old_dbq=dbq(Vars_of_old_dbq,Zf_of_old_dbq),	%a DBQ in the old list
	member(An_old_dbq,Old_dbq),
	member(V,Vars),					%shares a common var w/ the new one
	member(V,Vars_of_old_dbq),
	!,
	delete(Old_dbq,An_old_dbq,Remaining_dbq),	%remove it from old list
	my_union(Vars_of_old_dbq,Vars,Combined_vars),
	append_cif(Zf_of_old_dbq,Zf,Combined_zf),
	Combined_dbq=dbq(Combined_vars,Combined_zf),
	combine_dbq(Remaining_dbq,Combined_dbq,New_dbq).

combine_dbq(Old_dbq,Dbq,[Dbq|Old_dbq]).



%
% put var type into DBQ so that KRAFT can take it
%
put_type_into_dbq([],_,[]).

put_type_into_dbq([E|Es],Types,[D|Ds]) :-
	put_type_into_dbq(E,Types,D),
	put_type_into_dbq(Es,Types,Ds).

put_type_into_dbq(dbq(Vars,Zf),Types,dbq(Vars:Type_list,Zf)) :-
	construct_type_list(Vars,Types,Type_list).


%
% construct_type_list/3
%
% given a list of Daplex vars, return a list of their types
%
construct_type_list([],_,[]).

construct_type_list([V|Vs],Types,[T|Ts]) :-
	member(V:T,Types),
	construct_type_list(Vs,Types,Ts).


