%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FUZZY XPATH -
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The predicate has the following form
%                  fuzzyXPath(Consult, Element, Deep, Down, Result)
%
% Consult : Consultation has a list structure, which is touring until empty.
% Element : Structure of SWI-Prolog used to handle XML files, has the form:
%        [element(Tag, Attributes, Sons)|Siblings],
% 	      Tag, Node name
% 	      Attributes, Attributes of the current node, TAG.
% 	      Sons, List containing the sons of current element
% 	      Siblings, List of siblings of current node
%   Deep : Horizontal penalization value, when is deepened in the XML Tree
%   Down : Vertical penalization value, below... :D
% Result : Out the predicate, output with TV structure
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% TV, Truth value, the structure is handled as follows
%
%	tv(valorVerdad,[currentTag, Son, Sibling]), where both the son and the brother has a TV structure.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Stop functions
% It arrived at the end of the tree
fuzzyXPath(_, [], _, _, tv(0.0,[])):-!.

% The consultations is empty
fuzzyXPath([], _, _, _, tv(0.0,[])):-!.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([absolutePath|LabelRest], Element, Deep, Down, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV_Iam).

fuzzyXPath([debug(_)|LabelRest], Element, Deep, Down, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To change the deep & down ...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% For Deep and Down
fuzzyXPath([conDeDo(Deep,Down)|LabelRest], Element, _, _, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV1),
	    and_prod(tv(1.0,[]), TV1, TV_Iam).

% For Down
fuzzyXPath([conDown(Down)|LabelRest], Element, Deep, _, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV1),
	    and_prod(tv(1.0,[]), TV1, TV_Iam).

% For Deep
fuzzyXPath([conDeep(Deep)|LabelRest], Element, _, Down, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV1),
	    and_prod(tv(1.0,[]), TV1, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To get the attribute.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([Label, attr(Attribute)], [element(Label, Attr, _)|Siblings], Deep, Down, TV_Iam):-!,
	    attribute_values(Attr, Attribute, TV_Attr), % -->
	    fuzzyXPath([Label,attr(Attribute)], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(TV_Attr, tv(0.0,[]), TV_Sib0, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To get the Text
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([Label, text], [element(Label, _, Text)|Siblings], Deep, Down, TV_Iam):-!,
	    fuzzyXPath([Label,text], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(tv(1.0, [Text,[],[]]), tv(0.0,[]), TV_Sib0, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Allows for the evaluation of queries.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([Label,tree(A,B,C)], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    execute_fcond(Label, tree(A,B,C), element(Label,Attr,Sons), tv(TV, _)),!,
	    fuzzyXPath([Label, tree(A,B,C)], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    ((TV = 0.0, agr_fuse(tv(1.0,[]), tv(0.0,[]), TV_Sib0, TV0));
	     (agr_fuse(tv(1.0, [tv(TV, []), element(Label, Attr, Sons)]), tv(0.0,[]), TV_Sib0, TV0))
	    ).
/*
fuzzyXPath([Label,tree(A,B,C)], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    execute_fcond(Label, tree(A,B,C), element(Label,Attr,Sons), tv(TV, _)), !,
	    fuzzyXPath([Label, tree(A,B,C)], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
            agr_fuse(tv(1.0, [tv(TV, []), element(Label, Attr, Sons)]), tv(0.0,[]), TV_Sib0, TV0).
*/

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Similar to the previous
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To swap
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([swap(Swap)|LabelRest], Element, Deep, Down, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Element, Deep, Down, TV1),
	    and_prod(tv(Swap,[]), TV1, TV_Iam1),
   	    agr_fuse(tv(1.0, []), TV_Iam1, tv(0.0,[]), TV_Iam).

fuzzyXPath([Label,swap(Swap),attr(Attribute)], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    fuzzyXPath([Label, attr(Attribute)], [element(Label,Attr,Sons)], Deep, Down, TV_Son),
	    fuzzyXPath([Label, swap(Swap),attr(Attribute)], Siblings, Deep, Down, TV_Sib),

            agr_fuse(tv(Swap, []), TV_Son, TV_Sib, TV0).

% for attribute --- review many times....
fuzzyXPath([Label,tree(A,B,C),attr(Attribute)], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    execute_fcond(Label, tree(A,B,C), element(Label,Attr,Sons), tv(TV, _)), !,
	    attribute_values(Attr, Attribute, tv(1.0,TV_Attr)), % -->
	    fuzzyXPath([Label, tree(A,B,C),attr(Attribute)], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    ((TV=0.0, agr_fuse(tv(1.0, []), tv(0.0, []), TV_Sib0, TV0));
	     (agr_fuse(tv(1.0, [tv(TV, []),TV_Attr]), tv(0.0,[]), TV_Sib0, TV0))
	    ).


/*
fuzzyXPath([Label,tree(A,B,C)|LabelRest], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    execute_fcond(Label, tree(A,B,C), element(Label,Attr,Sons), tv(0.0, _)), !,
	    fuzzyXPath([Label, tree(A,B,C)|LabelRest], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),

            agr_fuse(tv(1.0, []), tv(0.0, []), TV_Sib0, TV0).
*/

fuzzyXPath([Label,tree(A,B,C)|LabelRest], [element(Label,Attr,Sons)|Siblings], Deep, Down, TV0):-
	    execute_fcond(Label, tree(A,B,C), element(Label,Attr,Sons), tv(TV, _)), !,
	    fuzzyXPath([Label, tree(A,B,C)|LabelRest], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    ((TV=0.0, agr_fuse(tv(1.0, []), tv(0.0, []), TV_Sib0, TV0));
	     (fuzzyXPath(LabelRest, Sons, Deep, Down, TV_Son),
	      agr_fuse(tv(1.0, [tv(TV, [])]), TV_Son, TV_Sib0, TV0))
 	    ).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% relative path, double slash //
% queries of the form //tag1/tag2/..../tagn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([relativePath|Label], [element(TagIam,Attr,Sons)|Siblings], Deep, Down, TV0):-!,

	    % puede tener la forma TV_Iam = tv(0,[]); tv(TV,[Elem, TvSon,[]])
	    fuzzyXPath(Label, [element(TagIam,Attr,Sons)], Deep, Down, TV_Iam), % not sent the siblings!!!!

	    % TV_Son = tv(0,[]); tv(TV,[Elem, Son, Bro])
	    fuzzyXPath([relativePath|Label], Sons, Deep, Down, TV_Son),
	    and_prod(Deep, TV_Son, TV_Son0),

	    fuzzyXPath([relativePath|Label], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),

	    agr_fuse(TV_Iam, TV_Son0, TV_Sib0, TV0).
%	    agr_fuse(tv(1.0,[]), TV_Son0, TV_Bro0, TV_A1),
%	    agr_fuse(tv(1.0,[]), TV_Iam, TV_A1, TV0).

fuzzyXPath([relativePath| _], [_], _, _, tv(0.0,[])):-!.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% when there is only one element in the list.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% When it matches the last tag that we search
fuzzyXPath([Label|[]], [element(Label, Attr, Sons)|Siblings], Deep, Down, TV_Iam):- !,
	    fuzzyXPath([Label|[]], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(tv(1.0, element(Label, Attr, Sons)), tv(0.0,[]), TV_Sib0, TV_Iam).

% when it not matches the last tag that we search
fuzzyXPath([Label|[]], [_|Siblings], Deep, Down, TV_Iam):- !,
	    fuzzyXPath([Label|[]], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(tv(1.0, []), tv(0.0,[]), TV_Sib0, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Traveling through the consultation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fuzzyXPath([Label|LabelRest], [element(Label,_,Sons)|Siblings], Deep, Down, TV_Iam):-!,
	    fuzzyXPath(LabelRest, Sons, Deep, Down, TV_Son),
	    fuzzyXPath([Label|LabelRest], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(tv(1.0, []), TV_Son, TV_Sib0, TV_Iam).

% when it not matches the last tag
fuzzyXPath([Label|LabelRest], [_|Siblings], Deep, Down, TV_Iam):- !,
	    fuzzyXPath([Label|LabelRest], Siblings, Deep, Down, TV_Sib),
	    and_prod(Down, TV_Sib, TV_Sib0),
	    agr_fuse(tv(1.0, []), tv(0.0,[]), TV_Sib0, TV_Iam).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FUZZY LOGIC OPERATORS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% for AND
execute_fcond(Label, tree(and, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				and_prod(TV1, TV2, TV0).
% for AND Luka
execute_fcond(Label, tree(and_luka, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				and_luka(TV1, TV2, TV0).
% for AND Godel
execute_fcond(Label, tree(and_godel, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				and_godel(TV1, TV2, TV0).

% for OR
execute_fcond(Label, tree(or, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				or_prod(TV1, TV2, TV0).

% for OR Luka
execute_fcond(Label, tree(or_luka, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				or_luka(TV1, TV2, TV0).

% for OR Godel
execute_fcond(Label, tree(or_godel, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				or_godel(TV1, TV2, TV0).
% for AVG
execute_fcond(Label, tree(avg, T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				agr_avg(TV1, TV2, tv(1,[]), tv(1,[]), TV0).

% for AVG-Extended
execute_fcond(Label, tree(avg(A,B), T1, T2), element(Label,Attr,Sons), TV0):- !,
	                        execute_fcond(Label, T1, element(Label,Attr,Sons), TV1),
				execute_fcond(Label, T2, element(Label,Attr,Sons), TV2),
				agr_avg(TV1, TV2, tv(A,[]), tv(B,[]), TV0).

% When there is only one element.
execute_fcond(Label, tree(Exp,nil,nil), element(Label,Attr,Sons), TV0):- !,
	                        execute_feq(Label, Exp, element(Label,Attr,Sons), TV1),
				and_prod(TV1, tv(1.0,[]), TV0).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RELATIONAL OPERATORS
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% A predicate more general for execute_feq
execute_feq(Label, op(Operation, A,B), element(Label,Attr,Sons), TV0):- !,
	fuzzyXPath([Label|A], [element(Label, Attr, Sons)], tv(1,[]), tv(1,[]), TV1), %returns list of all the values found
	agr_cond(Operation, TV1, tv(1.0, B), TV0).

execute_feq(Label, exist(A), element(Label,Attr,Sons), TV0):- !,
	fuzzyXPath([Label|A], [element(Label, Attr, Sons)], tv(1,[]), tv(1,[]), TV1), %returns list of all the values found
	((TV1=tv(0.0,[]), TV0=TV1);
	 (TV0=tv(1.0,[]))),!.




%%%%%%%%%%%%%%%%%%
% ATTRIBUTE-VALUES
%%%%%%%%%%%%%%%%%
%
% attribute_values(Elem_Att, Qy_Att, TV_Att)
%
% Retorna un arreglo con los valores de todos los atributos
% iguales a Qy_att
%
% Elem_Att = attributes of the current element
% Qy_Att = Attribute that is searched,
% TV_Att = Result,

attribute_values([],                _,	 tv(1.0, [])):-!.
attribute_values(['='(Att,V)|RAtt], Att, tv(1.0, [V|VR])):-!, attribute_values(RAtt, Att, tv(1.0, VR)).%, name(V,LV), name(NV,LV).
attribute_values([_|RAtt],          Att, RV):- attribute_values(RAtt, Att, RV).


















