:- multifile pfd_demo:term_expansion/2.
:- multifile pfd_demo:goal_expansion/3.

:- dynamic pfd_demo:term_expansion/2.
:- dynamic pfd_demo:goal_expansion/3.

% term_expansion/2 , modified here, is used to read-in programs,
% thus last clause should always be that.

register_pfd_predicate( Head ) :-
	( pfd_predicate( Head ) ->
		true
		;
		Head =.. [Name|Args],
		length( Args, N ),
		is_list_of_n_vars( N, Vars ),
		EmptyHead =.. [Name|Vars],
		assertz( pfd_predicate(EmptyHead) )
	).

expand_constraint( (C), C ).

expand_dependent( Dep, Op, ExpDep ) :-
	% Caller = Dep,
	( (Dep = (Left ++ Rest),Op=(++)) -> 
		Left = (LfDepPd::LfPi),
		% in_num_to_fraction( LfPi, Caller, Fract ),
		ExpDep = [LfDepPd:LfPi|T],
		% ExpDep = [LfDepPd:Fract|T],
		expand_dependent( Rest, Op, T )
		;
		( (Dep = (Left \> Rest),Op=(\>)) ->
			Left = (LfDepPd::LfPi),
			% in_num_to_fraction( LfPi, Caller, Fract ),
			ExpDep = [LfDepPd:LfPi|T],
			% ExpDep = [LfDepPd:Fract|T],
			expand_dependent( Rest, Op, T )
			;
			Dep = (LfDepPd::LfPi),
			% the following brakes, 1-With, that
			% converting this to a fraction should
			% be left to runtime
			% in_num_to_fraction( LfPi, Caller, Fract ),
			% ExpDep = [LfDepPd:Fract],
			ExpDep = [LfDepPd:LfPi],
			( var(Op) -> Op = (++); true )
		)
	).

expand_pfd_goal( (Left\\(PiIn::Right)), conditional([ExpLft:PiIn,(\+ ExpLft):(1 - PiIn)],++,ExpRgt) ) :-
% expand_pfd_goal( (Left\\(PiIn::Right)), conditional(ExpLft,Pi,ExpRgt) ) :-
	expand_constraint( Left, ExpLft ),
	expand_constraint( Right, ExpRgt ).
	% Caller = (Left\\Pi::Right).
	% in_num_to_fraction( PiIn, Caller, Pi ).

expand_pfd_goal( (Left\\Right), conditional(ExpLft,Op,ExpRgt) ) :-
	expand_dependent( Left, Op, ExpLft ),
	expand_constraint( Right, ExpRgt ).
expand_pfd_goal( (Left\\Right), conditional([ExpLft:1/1],++,ExpRgt) ) :-
	expand_constraint( Left, ExpLft ),
	expand_constraint( Right, ExpRgt ).

expand_pfd_goal( (VarL\#Right), cond_diff(VarL,Pi,VarR) ) :-
	( var(Right) -> 
		VarR = Right, Pi = 1/1 
		;
		Right = (Pi::VarR)
		% Caller = (VarL\#Right)
		% in_num_to_fraction( PiIn, Caller, Pi )
	).
	
% expand_pfd_goal( (Var pin Method), pin(Var,Method) ).
% expand_pfd_goal( (Var pin Method), prin(Var,MethodStr) ) :-
	% !,
	% ( var(Method) -> 
		% MethodStr = Method
		% ;
		% expand_method( Method, MethodStr )
	% ).

expand_pfd_goal( Var is p(Predicate), prob(Predicate,Var)  ).
% expand_pfd_goal( Var pin Method, pin(Var,Method)  ).
% expand_pfd_goal( Var is p(Constr), prob(Constr,Var)  ).
	% expand_constraint( Constr, ExpCnstr ).

% expand_pfd_goal( Other, Expanded ) :-
	% clpfd:compile( Other, 0, Info, Expanded ),
	% !,
	% write( clpfd:compile( Other, 0, Info, Expanded ) ),
	% abort.
% expand_pfd_goal( Var fpin Method, (fpin(Var,Method),FdIn) ) :-
	% method_to_indexical( Method, Indexical ),
	% goal_expansion( Var in Indexical, _, FdIn ). 

expand_pfd_goal( AnythingElse, SomethingElse ) :-
	goal_expansion( AnythingElse, _, SomethingElse ), 
	!.
expand_pfd_goal( AnythingElse, AnythingElse ).

% expand_method( (Method # Var with Prob), (Method,Var,cond_diff,Prob) ).
	% expand_term( Prob, ExpProb ).
expand_method( Method, Method ).

expand_body( (Goal,MoreGoals), [ExpGoal|ExpMoreGoals] ) :-
	expand_pfd_goal( Goal, ExpGoal ),
	expand_body( MoreGoals, ExpMoreGoals ).

expand_body( Goal, [ExpGoal] ) :-
	\+ Goal = (_A,_B),
	expand_pfd_goal( Goal, ExpGoal ).

term_expansion( Clause, PfdClause ):-
% term_expansion( Clause, (:- true) ):-
	\+ Clause = (?- _Querry),
	\+ Clause = (:- _Directive),
	\+ Clause = end_of_file,
	( Clause = (Head:-Goals)  ->
			% clpfd:fd_expansion(Head, Goals, 0, C),
			% write( C ), abort,
		( pfd_hookable_predicate_head(Head) ->
			% assertz( (Head:-Goals) )
			PfdClause = Clause
			;
			expand_body( Goals, ExpGoals ),
			ensure_list( ExpGoals, ListOfGoals ),
			% assertz( pfd_rule(Head,ListOfGoals) )
			register_pfd_predicate( Head ),
			PfdClause = [(:- multifile pfd_rule/2),pfd_rule(Head,ListOfGoals)]
		)
		;
		% this *should* deal with facts
		( pfd_hookable_predicate_head(Clause) ->
			% assertz( (Clause) )
			PfdClause = Clause
			;
			% assertz( pfd_rule(Clause,[]) )
			register_pfd_predicate( Clause ),
			PfdClause = [(:- multifile pfd_rule/2),pfd_rule(Clause,[])]
		)
	).
