/*
   Sandro's experiments in converting RDF-Model logics into prolog 
   rules...   At some point this should evolve into a nice library.

  cool thing:
     xsb -e "[semweb]. assert_n3(ht2)."
     xsb -e "[semweb]. assert_n3(gedcom_damlex)."
  maybe
     xsb -e "[gedcom_damlex_rules]."

*/

% use this to generate a warning
:- auto_table.

:- multifile property/3.    % tproperty causes problems.  Hrm.

:- table tproperty/3.
tproperty(A,B,C) :- property(A,B,C). 


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   Prolog utilities
%

% Turn a Prolog list into a comma-separated list
comma_list(CL, [H]) :-
   CL = H.
comma_list(CL, [H|T]) :-
   comma_list(CLT, T),
   CL = ( H , CLT ).

% Connect two lists via a predicate between each element
% (borrowed from Clocksin/Mellish) 
:- table maplist/3.
maplist(_, [], []).
maplist(P, [X|L], [Y|M]) :-
   Q =.. [ P, X, Y ],
   call(Q),
   maplist(P, L, M).

:- table maplist/4.
maplist(_, [], [], _).
maplist(P, [X|L], [Y|M], Extra) :-
   Q =.. [ P, X, Y, Extra ],
   call(Q),
   maplist(P, L, M, Extra).

:- table maplist/5.
maplist(_, [], [], _, _).
maplist(P, [X|L], [Y|M], Extra, Extra2) :-
   Q =.. [ P, X, Y, Extra, Extra2 ],
%   write('  ... calling '), writeln(Q),
   call(Q),
   maplist(P, L, M, Extra, Extra2).
	   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RDF Collections     
%

%  list_gen(prolog-list, list-id) succeeds if it can build the
%  prolog-list to match the DAML-list.   Can't work backwords because
%  it can't add RDF triples.
%
:- table list_gen/2.
list_gen([H|T], L) :-
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#first', H),
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#rest', TX),
   list_gen(T, TX).
list_gen([H], L) :-             
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#first', H),
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#rest', 'http://www.daml.org/2001/03/daml+oil#nil').
list_gen([], L) :- 
   L = 'http://www.daml.org/2001/03/daml+oil#nil'.


% one approach
set_gen(Enumeration, S) :- 
   tproperty(S, 'http://www.w3.org/2001/04/18/set#enumeration', L),
   list_gen(Enumeration, L).

:- import memberchk/2 from basics.

% another
in_set(Element, SetID) :-
   tproperty(SetID, 'http://www.w3.org/2001/04/18/set#enumeration', ListID),
   list_gen(Enumeration, ListID),
   memberchk(Element, Enumeration).

%
%  Set Membership which does NOT rely on Negation-As-Failure (NAF).
%  With known_in_set and known_not_in_set, you fail (to prove
%  anything) if you don't know which it is.   I'm unclear on whether
%  this is useful.
%
known_in_set(Element, SetID) :-
   tproperty(SetID, 'http://www.w3.org/2001/04/18/set#enumeration', ListID),
   in_list(Element, ListID).
known_not_in_set(Element, SetID) :-
   tproperty(SetID, 'http://www.w3.org/2001/04/18/set#enumeration', ListID),
   not_in_list(Element, ListID).

in_list(_, '*daml:nil') :- fail.
in_list(Element, L) :- tproperty(L, 'http://www.daml.org/2001/03/daml+oil#first', Element).
in_list(Element, L) :- tproperty(L, 'http://www.daml.org/2001/03/daml+oil#rest', T), in_list(Element, T).
not_in_list(Element, L) :-
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#first', H),
   tproperty(L, 'http://www.daml.org/2001/03/daml+oil#rest', T),
   H \= Element,               % lit comparison here!  ?! EquivalentOf ?!
   not_in_list(Element, T).
not_in_list(_, 'http://www.daml.org/2001/03/daml+oil#rest').
% To avoid the literal comparison, we really need to like both kinds
% of vars, I think....

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%       Turn described Horn Rules into Prolog Rules
%

% Makes P be a prolog structure like "tproperty(a,b,c)" which represents
% the same statement as S identifies in the current database. 

vtriple_gen(P, S, VarTable) :-
   tproperty(S,'http://www.w3.org/2001/04/19/horn#subject',SA),
   tproperty(S,'http://www.w3.org/2001/04/19/horn#predicate',SB),
   tproperty(S,'http://www.w3.org/2001/04/19/horn#object',SC),
   prolog_term(SAV, SA, VarTable),
   prolog_term(SBV, SB, VarTable),
   prolog_term(SCV, SC, VarTable),
   P = tproperty(SAV, SBV, SCV).

:- import append/3 from basics.
:- import memberchk/2 from basics.

% :- table prolog_term/3.
prolog_term(Term, Ident, _) :-
   tproperty(Ident, 'http://www.w3.org/2001/04/19/horn#nameAsConstant', Sym),
   % atom_codes(Sym, SymStr),
   % append("'", SymStr, S1),
   % append(S1, "'", TermStr),
   % atom_codes(Term, TermStr).
   Term = Sym.
prolog_term(Term, Ident, VarTable) :-
   tproperty(Ident, 'http://www.w3.org/2001/04/19/horn#nameAsVariable', Sym),
   % atom_codes(Sym, SymStr),
   % append("_VAR_", SymStr, TermStr),
   % atom_codes(Term, TermStr).
   memberchk(varbind(Sym, Term), VarTable).
prolog_term(Term, Ident, VarTable) :-
   tproperty(Ident, 'http://www.w3.org/2001/04/19/horn#listAsFunction', List),
   list_gen(LL, List),
   maplist(prolog_term, L , LL, VarTable),
   Term = skolem(L).   
   % or I could use univ (=..) and put it in a structure.   Hrm...

% rule_gen(R, RuleID) succeeds if it can create a prolog rule
% structure in R which has the same meaning as the rule named
% RuleID described in the database.

rule_gen(R, RuleID, VarTable) :-
   tproperty(RuleID, 'http://www.w3.org/2001/04/19/horn#premise', Prem),
   tproperty(RuleID, 'http://www.w3.org/2001/04/19/horn#conclusion', Conc),
   vtriple_gen(Head, Conc, VarTable),
   set_gen(PremListOfIDs, Prem),
   maplist(vtriple_gen, PremList, PremListOfIDs, VarTable),
   comma_list(Tail, PremList),
   R = ( Head :- Tail ).

:- table comma_list/2.
:- table in_list/2.
:- table not_in_list/2.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%       Turn described TimBL's n3 logic Rules into Prolog Rules
%

% Makes P be a prolog structure like "tproperty(a,b,c)" which represents
% the same statement as S identifies in the current database. 

n_triple_gen(P, S, VarTable, ContextID) :-
   tproperty(S,'http://www.w3.org/1999/02/22-rdf-syntax-ns#subject',SA),
   tproperty(S,'http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate',SB),
   tproperty(S,'http://www.w3.org/1999/02/22-rdf-syntax-ns#object',SC),
   n_prolog_term(SAV, SA, VarTable, ContextID),
   n_prolog_term(SBV, SB, VarTable, ContextID),
   n_prolog_term(SCV, SC, VarTable, ContextID),
   P = property(SAV, SBV, SCV).            %   THIS IS THE OUTPUT FORM

n_prolog_term(Term, Ident, VarTable, ContextID) :-
   univar(Ident, ContextID), memberchk(varbind(Ident, Term), VarTable).
n_prolog_term(Term, Ident, _, ContextID) :-
   % shuuld be CONST-CHECK 		    
   tnot(univar(Ident, ContextID)), Term = Ident.

:- table univar/2.

%  X is a universal variable in model M if...
%  (1) in M (or some surrounding context) it's been declared
%      forall that context
univar(X,M) :- 
   supermodel(M, Outer),
   dataset(Outer, '', 'http://www.w3.org/2000/10/swap/log#forAll', X).

univar(X,M) :- 
   supermodel(M, Outer),
   dataset(Outer, 'this', 'http://www.w3.org/2000/10/swap/log#forAll', X).

%  (2) in some surrounding context, it's been declared as 
%      forall this model
univar(X,M) :-
   supermodel(M, Outer),
   dataset(Outer, M, 'http://www.w3.org/2000/10/swap/log#forAll',
   X).

supermodel(Inner, Outer) :-
  dataset(Outer, Inner, 'http://www.w3.org/2001/04/18/set#enumeration', _).
supermodel(Inner, Outer) :-
  Inner = Outer.
strictlysupermodel(Inner, Outer) :-
  dataset(Outer, Inner, 'http://www.w3.org/2001/04/18/set#enumeration', _).

rule_gen(R, _, VarTable) :-
   tproperty(Prem, 'http://www.w3.org/2000/10/swap/log#implies', Conc),
   in_set(ThisConc, Conc),
   n_triple_gen(Head, ThisConc, VarTable, Conc),   
   % write(' head: '),
   % writeln(Head),
   set_gen(PremListOfIDs, Prem),
   % write(' premise ids: '), writeln(PremListOfIDs),
   maplist(n_triple_gen, PremList, PremListOfIDs, VarTable, Prem),
   comma_list(Tail, PremList),
   R = ( Head :- Tail ).

tproperty(A, B, C) :- 
   tproperty(ContextID, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://www.w3.org/2000/10/swap/log#Truth'),
   dataset(ContextID, A, B, C).

dataset(SetID, S, P, V) :-
   tproperty(Element, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#subject', S),
   tproperty(Element, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate', P),
   tproperty(Element, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#object', V),
   in_set(Element, SetID).
dataset('', S, P, V) :-
   tproperty(S,P,V).
dataset('this', S, P, V) :-
   tproperty(S,P,V).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   Evaluable Predicates:   Reading Files
%

% like consult, but dynamic.   
assert_file(File):-
  repeat,
  read(File, X), 
  assert(X),
  X = end_of_file,
  !.

basename(Basename, Name, OptionalSuffix) :-
  str_length(Name, Len), 
  str_length(OptionalSuffix, SuffLen),
  BaseLen is Len-SuffLen,
  substring(Name, BaseLen, Len, Suffix), 
  ( 
    Suffix = OptionalSuffix, substring(Name, 0, BaseLen, Basename) 
   ;
    Basename = Name 
  ).

/*  OLD VERSION
assert_n3(File) :- 
   !,
   dynamic(tproperty/3),
   basename(Basename, File, '.n3'),
   str_cat(Basename, '.P', Pname),   
   shell(['yacc-n3 -p ', Basename, '.n3 >', Pname]),
   % assert_file(Pname),
   load_dyn(Pname),
   findall(EachRule, 
     (rule_gen(R, EachRule, _), assert((R)),
      write('Rule '),
      write(EachRule),
      writeln(' translated as: '),
      write('   '),
      writeq(R),
      writeln('')
     ), L),
   write('Rules Added: '),
   writeln(L).
*/

assert_n3(File) :-
   n3_to_xsb(File, Pname, Rname),
   load_dyn(Pname),
   shell(['ls -l ',Rname]),
   shell(['cat -n ',Rname]),
   consult(Rname, [verbo]).

n3_to_xsb(File) :- n3_to_xsb(File, _, _).
n3_to_xsb(File, Pname, Rname) :-
   % how to not affect the database???
   basename(Basename, File, '.n3'),
   str_cat(Basename, '_facts.P', Pname),   
   str_cat(Basename, '_rules.P', Rname),   
   shell(['yacc-n3 -p ', Basename, '.n3 >', Pname]),
   % assert_file(Pname),
   recognize_rules(Pname, Rname).

recognize_rules(Pname, Rname) :-
   load_dyn(Pname),
   writeln(Rname, '%  Generated by recognizing rules....'),
   writeln(Rname, '% :- dynamic property/3.'),
   writeln(Rname, ':- auto_table.'),
   writeln(Rname, 'tproperty(A,B,C) :- property(A,B,C).'), 
   findall(EachRule, 
     (rule_gen(R, EachRule, _), 
%      write('Rule '),
%      write(EachRule),
%      writeln(' translated as: '),
%      write('   '),
%      writeq(R),
      (atom(EachRule) -> write(Rname, '% Rule identified as '),
                         writeln(Rname, EachRule)
                      ;  writeln(Rname, '% Anonymous rule')),
      R = ((Left :- Right)),
      writeq(Rname, Left),
      writeln(Rname, ' :- '),
      write(Rname, '   '),
      writeq(Rname, Right),
      writeln(Rname, '.') 
%      writeln('')
     ), L),
   close(Rname),
   write('Rules Written: '),
   writeln(L).
  
% xsb -e "[semweb].  n3_to_xsb('gedcom-damlex').  " 

write_n3 :- seeing(F), write_n3(F).
write_n3(F) :- 
   tproperty(S, P, O),
   write(F, '<'), write(F, S), write(F, '> '),
   write(F, '<'), write(F, P), write(F, '> '),
   write(F, '<'), write(F, O), writeln(F, '>.'),
   fail.
write_n3(_).

% assert((tproperty(a,X,Y) :- tproperty('genid:12', X, Y))).

% xsb -e "[semweb].   assert_n3(ht2).  write_n3('ht2.out').  halt."

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/*

% I wonder if THIS will work!
get_clean_name(Name, ID, UsedNamesList) :-
  var(Name),
  memberchk(bind(ID, Name), UsedNamesList),
  (var(Name) -> 
     clean_name(Name, ID, Iter),
     not memberchk(bind(Any, Name), UsedNamesList)
  ).

clean_name(Name, ID, Iter) :-
   % do something about ID -- some prolog ordering hack?		 
   clean_name(Name, ID).

:- import re_match/5, re_substitute/4, re_charlist_to_string/2 from regmatch.
:- import intern_string/2 from machine.

clean_name(Name, ID) :-
   reverse(Name, RName),
   
*/

%  assert(property(a,b,c)).
%  assert(property(b, 'http://www.daml.org/2001/03/daml+oil#equivalentTo', bb)).
%  tproperty(a,X,c).

 
