
% DCA back end.

:- dynamic( fact/2 ).
:- dynamic( pred/2 ).

% init
% ----

dca_init :- 
   global_abolish( fact/1 ),
   global_abolish( pred/1 ).

% interface for setting the fact/pred data.
% F is a list of predicates, each predicate
% is a name followed by the key data.
% e.g. [ [married, false, true], [male, false] ].
% -----------------------------------------

dca_assert_pred( F, A, K ) :-
    global_assert( pred( F/A, K ) ).

% interface to assert/retract a fact
% ----------------------------------

copy_assert_to_connected( PT ) :-
    cs_method( 'DCAServer.DCAServer', 'BroadcastAssert'( PT ), _ ).
    
copy_retract_to_connected( PT ) :-
    cs_method( 'DCAServer.DCAServer', 'BroadcastRetract'( PT ), _ ).

get_term_jjc( T, TO ) :-
    cs_method( 'DCAServer.DCAServer', 'GetStringReader'( T ), SR ),
    read( SR, TO ).

dca_assert_no_copy( T ) :-
    get_term_jjc( T, PT ),  
    functor( PT, F, A ), !,
    \+( global_call( fact( F/A, PT ) ) ),
    global_assert( fact( F/A, PT ) ).
    
dca_assert_no_copy( T ) :-
    get_term_jjc( T, PT ),
    \+( global_call( fact( PT/0, PT ) ) ),
    global_assert( fact( PT/0, PT ) ).

dca_assert_with_conflict_detect( F/A, PT, T ) :-
    global_assert( fact( F/A, PT ) ),
    backtrackable_lock( F/A ),
    findall( FF, global_call( fact( F/A, FF ) ), L ),
    ( \+( any_conflicts( L ) ) ->
      global_retract( fact( F/A, PT ) )
     ;
      copy_assert_to_connected( T )
    ).

dca_assert( T ) :-
    get_term_jjc( T, PT ),  
    functor( PT, F, A ), !,
    dca_assert_with_conflict_detect( F/A, PT, T ).

dca_assert( T ) :-
    get_term_jjc( T, PT ),
    dca_assert_with_conflict_detect( PT/0, PT, T ). % fixed bug 11/2/2003

dca_assert_on_server( T ) :-
    get_term_jjc( T, PT ),  
    functor( PT, F, A ), !,
    global_assert( fact( F/A, PT ) ).

dca_assert_on_server( T ) :-
    get_term_jjc( T, PT ),
    global_assert( fact( PT/0, PT ) ).

dca_retract( T ) :-
    get_term_jjc( T, PT ),
	functor( PT, F, A ), !,
    global_retract( fact( F/A, PT ) ),
    copy_retract_to_connected( T ).

dca_retract( T ) :-
    get_term_jjc( T, PT ),
    global_retract( fact( PT/0, PT ) ),
    copy_retract_to_connected( T ).

dca_retract_on_server( T ) :-
    get_term_jjc( T, PT ),
	functor( PT, F, A ), !,
    global_retract( fact( F/A, PT ) ).
    
dca_retract_on_server( T ) :-
    get_term_jjc( T, PT ),
    global_retract( fact( PT/0, PT ) ).
    
dca_special_retract( PT ) :-
    functor( PT, F, A ), !, 
    global_retract( fact( F/A, PT ) ).
    
dca_special_retract( PT ) :-
    global_retract( fact( PT/0, PT ) ).

%====================================================================
% The new scheme: (> 2 conflicts)
%====================================================================  

resolve_all( Reader, Writer ) :-
   findall( P, global_call( pred( P, _ ) ), L ),
   resolve_all_in_list( Reader, Writer, L, WaitVar ),
   wait_for( WaitVar ).
resolve_all( _, _ ).

resolve_all_in_list( R, W, [], WaitVar ) :- !.
resolve_all_in_list( R, W, [H|T], WaitVar ) :-
    resolve_predicate( H, R, W, WaitVar ),
    resolve_all_in_list( R, W, T, WaitVar ).

resolve_predicate( P, R, W, WaitVar ) :-
    backtrackable_lock( P ),
    findall( F, global_call( fact( P, F ) ), L ),
    \+( any_conflicts( L ) ),
    fork( resolve( L, R, W, WaitVar ) ),
    wait_for( WaitVar ).
resolve_predicate( _, _, _, _ ). % bug (I think) fixed 11/2/2003
    
check_for_conflict( [], [], [], _ ) :- !.
check_for_conflict( [H1|T1], [H2|T2], [Key|Tk], K ) :-
    Key = K,
    H1 = H2,
    check_for_conflict( T1, T2, Tk, K ).
check_for_conflict( [H1|T1], [H2|T2], [Key|Tk], K ) :-
    \+( Key = K ),
    check_for_conflict( T1, T2, Tk, K ).
   
there_are_some_keys( [] ) :- fail.
there_are_some_keys( [key|T] ) :- !.
there_are_some_keys( [nonkey|T] ) :- there_are_some_keys( T ).
   
conflict( P, X, Y ) :-
    X =.. [F1|A1], 
    Y =.. [F2|A2],
    global_call( pred( P, Keys ) ),
    there_are_some_keys( Keys ),
    check_for_conflict( A1, A2, Keys, key ),
    \+( check_for_conflict( A1, A2, Keys, nonkey ) ).

filter_conflicts( H, [], [] ) :- !.
filter_conflicts( H, [HH|T], [HH|T2] ) :-
    functor( H, F, A ),
    conflict( F/A, H, HH ), !, filter_conflicts( H, T, T2 ).
filter_conflicts( H, [HH|T], T2 ) :-
    filter_conflicts( H, T, T2 ).

any_conflicts( [] ) :- !.
any_conflicts( [H|T] ) :-
	write( H ), nl,
    filter_conflicts( H, T, C ), % one item, list to filter, resultant list.
    write( C ), nl,
    setdiff( T, C, D ),
    ( C = [] ->
      true
    ;
      !, fail % fail on finding first conflict.
    ),
    any_conflicts( D ).

resolve( [], R, W, WaitVar ) :- !.
resolve( [H|T], R, W, WaitVar ) :- 
	write( H ), nl,
    filter_conflicts( H, T, C ),
    write( C ), nl,
    setdiff( T, C, D ),
    ( C = [] ->
      true
    ; 
      resolve_conflict( [H|C], R, W, WaitVar )
    ),
    resolve( D, R, W, WaitVar ).

resolve_conflict( L, R, W, WaitVar ) :-
	list_to_object( L, LO ),
    cs_method( 'DCAServer.DCAServer', 'ResolveConflict'( LO, R, W ), N ),
    int_term_to_int( N, N2 ),
    retract_all_except_one( L, N2 ).
    
retract_all_except_one( L, N ) :-
    retract_all_except_one( L, 0, N ).
    
retract_all_except_one( [], _, _ ) :- !.
retract_all_except_one( [H|T], N, N ) :- !,    
    N1 is N + 1,
    retract_all_except_one( T, N1, N ).
retract_all_except_one( [H|T], M, N ) :-
    M1 is M + 1,
    dca_special_retract( H ),
    retract_all_except_one( T, M1, N ).

% setdiff( A, B, C ):  C = A \ B.  B is a correctly ordered subset of A.
% ----------------------------------------------------------------------

setdiff( L, [], L ) :- !.
setdiff( [H|T1], [H|T2], T3 ) :-
    setdiff( T1, T2, T3 ),
    !.
setdiff( [H1|T1], [H2|T2], [H1|T3] ) :-
    \+( H1 = H2 ),
    setdiff( T1, [H2|T2], T3 ).

% get all facts
% -------------

get_all_facts( L ) :-
    findall( F, global_call( fact( _, F ) ), L ).
    
% broadcast facts to all
% ----------------------

reset_all_agents :-
    cs_method( 'DCAServer.DCAServer', 'BroadcastAllRetract', _ ),
    get_all_facts( L ),
    list_to_object( L, LO ),
    cs_method( 'DCAServer.DCAServer', 'ResetAllPorts'( LO ), _ ).

reset_all_agents_OLD :- 
    cs_method( 'DCAServer.DCAServer', 'BroadcastAllRetract', _ ),
    get_all_facts( L ),
    broadcast_all_facts( L ).
    
broadcast_all_facts( [] ) :- !.
broadcast_all_facts( [H|T] ) :-
	write( 'broadcast: ' ), write( H ), nl,
    cs_method( 'DCAServer.DCAServer', 'BroadcastAssertTerm'( H ), _ ),
    broadcast_all_facts( T ).
    
% list difference
% ---------------

member( [H|T], H ) :- !.
member( [H|T], H2 ) :- member( T, H2 ).

diff( [], L2, [] ) :- !.
diff( [H|T], L2, [H|LO] ) :- \+( member( L2, H ) ), !, diff( T, L2, LO ).
diff( [H|T], L2, LO ) :- diff( T, L2, LO ).

