#!/usr/bin/env swipl -G12g

:- initialization main.

:- use_module(library(rdf_matcher)).
:- use_module(library(rdf_matcher/rule_inference)).

:- use_module(library(main)).
:- use_module(library(optparse)).
:- use_module(library(option)).

:- use_module(library(sparqlprog/io_utils)).
%:- use_module(library(rule_eval)).
:- use_module(library(index_util)).

:- use_module(library(sparqlprog/labelutils)).
:- use_module(library(semweb/rdf_library)).
:- use_module(library(semweb/rdf_http_plugin)).
:- use_module(library(semweb/rdf_cache)).
:- use_module(library(semweb/rdf_zlib_plugin)).
:- use_module(library(semweb/rdf11)).
:- use_module(library(semweb/rdfs)).

% note: when we switc to swipl 7.8 consider using new stack options
%:- set_prolog_flag(stack_limit,'0G').
%:- set_prolog_stack(global, limit(1)).

% TODO: make configurable
:- rdf_set_cache_options([ global_directory('RDF-Cache'),
                           create_global_directory(true)
                         ]).

main(Argv) :-
        catch(wmain(Argv),
              E,
              (   format(user_error,'~q~n',[E]),
                  halt(1))),
        halt.

wmain(Argv) :-
        Spec =
        [
         [opt(output), type(atom),
          longflags(['output']),
          shortflags([o]),
          help('Outfile')
         ],
         [opt(format), type(atom),
          longflags(['format']),
          shortflags([f]),
          help('Output format: csv')
         ],
         [opt(input), type(atom),
          longflags(['input']),
          shortflags([i]),
          help('Input RDF file (use in combo with -x)')
         ],
         [opt(indexdir), type(atom),
          longflags(['indexdir']),
          shortflags(['X']),
          help('path to directory to be used for materializing index')
         ],
         [opt(prefix), type(atom),
          longflags(['prefix']),
          shortflags([p]),
          help('Prefix to perform matching against')
         ],
         [opt(ontology), type(atom),
          longflags(['ontology']),
          help('Prefix of ontology to be compared against; note if using indexes this will be filtered prior')
         ],
         [opt(ontA), type(atom),
          longflags(['ontA']),
          help('Prefix1 of ontology in match (for learning)')
         ],
         [opt(ontB), type(atom),
          longflags(['ontB']),
          help('Prefix2 of ontology in match (for learning)')
         ],
         [opt(goal), type(term),
          longflags([goal]),
          shortflags([g]),
          help('Prolog goal to call')
         ],
         [opt(consult), type(atom),
          longflags([consult]),
          shortflags([c]),
          help('Prolog program to load/consult')
         ],
         [opt(use), type(atom),
          longflags([use]),
          shortflags([u]),
          help('Prolog module to use')
         ],
         [opt(use_no_import), type(atom),
          longflags([use_no_import]),
          shortflags(['U']),
          help('Prolog module to use, do not import all')
         ],
         [opt(debug), type(term),
          longflags([debug]),
          shortflags([d]),
          help('term passed to debug/1')
         ],
         [opt(attach), type(atom),
          longflags([attach]),
          shortflags(['A']),
          help('rdf_attach_library - path to void.ttl')
         ],
         [opt(service), type(atom),
          longflags([service]),
          shortflags([s]),
          help('name of remote service to query')
         ],
         [opt(inject_labels),
          type(boolean),
          default(false),
          longflags([label]),
          shortflags([l]),
          help('Inject query for rdfs labels into query')
         ],
         [opt(obsoletes),
          type(boolean),
          default(false),
          longflags([obsoletes]),
          help('If set, include obsoletes in results')
         ],
         [opt(show),
          type(boolean),
          default(false),
          longflags([show]),
          shortflags(['S']),
          help('Show SPARQL query')
         ],
         [opt(prolog),
          type(boolean),
          default(false),
          longflags([prolog]),
          shortflags(['P']),
          help('Interactive prolog')
         ],
         [opt(interactive),
          type(boolean),
          default(false),
          longflags([interactive]),
          shortflags(['I']),
          help('Interactive prolog')
         ],
         [opt(compile),
          type(boolean),
          default(false),
          longflags([compile]),
          shortflags(['C']),
          help('Compile Prolog to SPARQL (no execution)')
         ],
         [opt(verbose),
          type(boolean),
          default(false),
          longflags([verbose]),
          shortflags([v]),
          help('Same as --debug sparqlprog')
         ],
         [opt(stacktrace),
          type(boolean),
          default(false),
          longflags([stacktrace]),
          shortflags(['T']),
          help('Shows stack trace on error')
         ],
         [opt(execute),
          type(boolean),
          default(false),
          longflags([execute]),
          shortflags([e]),
          help('Executes query directly in prolog')
         ],
         [opt(query), type(term),
          longflags([query]),
          shortflags([q]),
          help('Prolog query')
         ]
        ],
        opt_parse(Spec, Argv, Opts, Rest, [duplicated_flags(keepall)]),
        handle_opts(Opts),
        debug(rdf_matcher,'Opts = ~q.',[Opts]),
        inject_prefixes,
        option(indexdir(IndexDir),Opts,none),
        index_pairs(IndexDir),
        opt_if_call(interactive,sparqlprog_shell(Opts),Opts),
        opt_if_call(prolog,prolog_shell(Opts),Opts),
        run(Rest, Opts).


run([learn|_Args], Opts) :-
        materialize_index(eq_from_match(+,+,-,-,-,+,+)),
        optional(materialize_index(eq_from_shared_xref(+,+,-,+,+))),
        debug(rdf_matcher,'Grounding',[]),
        ground_rules(Rules, Opts),
        debug(rdf_matcher,'Eval rules',[]),
        eval_and_show_rules(Rules).

run([classify, WeightFile | _Args], Opts) :-
        use_module(library(rule_eval)),
        consult(WeightFile),
        forall(infer_fact(equivalent(X,Y),_,W),
               write_result(eq(X,Y,W,foo),Opts)).
%               format('eq\t~w\t~w\t~w\tlearned~n',[X,Y,W])).

run([match|_Args], Opts) :-
        G = inter_pair_match(X,_,_,_Info),
        write_all_results(X,G,Opts).

run([exact|_Args], Opts) :-
        write_result_wrap(m(c1,c2,c1parents,c2parents,conf,match,info,alt_c1,alt_c2,ignored_c1,ignored_c2),Opts),
        G = exact_inter_pair_match(X,_,_,_,_,_,_,_,_,_,_),
        write_all_results(X,G,Opts).

run([new_match|_Args], Opts) :-
        G = new_pair_match(X,_,_,_Info),
        write_all_results(X,G,Opts).        

run([tri_match|_Args], Opts) :-
        G = tri_match(C1,_C2,_C3,_Info),
        write_all_results(C1,G,Opts).        
run([triad_nc|_Args], Opts) :-
        G = new_unique_match_triad_nc(C1,_C2,_C3),
        write_all_results(C1,G,Opts).        

run([rightnew_match|_Args], Opts) :-
        G = rightnew_pair_match(X,_,_,_Info),
        write_all_results(X,G,Opts).        

run([unique_match|_Args], Opts) :-
        G = new_unique_pair_match(X,_,_,_Info),
        write_all_results(X,G,Opts).

run([cluster|_Args], Opts) :-
        materialize_index(new_pair_match(+,+,-,-)),
        %materialize_index(transitive_new_match(+,+)),
        G = transitive_new_match_set_pair(X,_,_),
        write_all_results(X,G,Opts).

run([ucluster|_Args], Opts) :-
        materialize_index(new_unique_pair_match(+,+,-,-)),
        %materialize_index(transitive_unique_match(+,+)),
        G = transitive_unique_match_set_member(X,_),
        write_all_results(X,G,Opts).

run([trial|_Args], Opts) :-
        materialize_index(new_unique_pair_match(+,+,-,-)),
        G = new_unique_match_triad(X,_,_),
        write_all_results(X,G,Opts).

run([unmatched,Pfx|_Args], Opts) :-
        G = (unmatched_in(X,Pfx),has_prefix(X,XPfx)),        
        forall((G,obj_label(X,XN)),
               write_result(unmatched(X,XN,Pfx,XPfx),Opts)).

%! write_all_results(?X, +Goal, +Opts:list) is det.
%
% writes value of X for every Goal
%
% also filters based on Opts
write_all_results(X,G,Opts) :-
        debug(rdf_matcher,'Goal=~q',[G]),
        opt_query_constraint_pre(X,PreG,Opts),
        opt_query_constraint_post(X,G,PostG,Opts),
        forall((PreG,G,PostG),
               write_result_wrap(G,Opts)).

write_result_wrap(G,Opts) :-
        select(inject_labels(Inject),Opts,Opts2),
        nonvar(Inject),
        Inject,
        !,
        row_labelify(G,G2),
        write_result_wrap(G2,Opts2).
write_result_wrap(G,Opts) :-
        write_result(G,Opts).

obj_label(X,N) :-
        rdf(X,rdfs:label,N),
        !.
obj_label(_,'') :- !.


% Unify G with a goal that succeeds
% if X passes criteria specified in Opts.
%
% these are intended to be executed prior to
% matching
opt_query_constraint_pre(X,G,Opts) :-
        option(prefix(Prefix),Opts),
        nonvar(Prefix),
        !,
        G=obj_has_prefix(X,Prefix).
opt_query_constraint_pre(_,true,_).

% as above, but executed after the fact
opt_query_constraint_post(_,G,PostG,Opts) :-
        option(obsoletes(Obs),Opts),
        Obs=false,
        G =.. [_,A,B|_],
        !,
        PostG = (is_not_obsolete(A),is_not_obsolete(B)).
opt_query_constraint_post(_,_,true,_).

is_not_obsolete(X) :-
        \+ ((rdf_is_iri(X),rdf(X,owl:deprecated,_))).

eval_and_show_rules(Rs) :-
        use_module(library(rule_eval)),
        debug(rdf_matcher,'Eval rules: ~q',[Rs]),
        eval_rules(Rs,Pairs),
        debug(rdf_matcher,'Evaled all rules',[]),
        forall(member(P,Pairs),
               show_rule(P)).

:- op(1200, xfy, ::).

show_rule(R-Scores) :-
        memberchk(pr_true(Pr),Scores),
        wrule( Pr::R ),
        format('%% Scores: ~q.~n',[Scores]),
        nl.

wrule(R) :-
        copy_term(R,R2),
        numbervars(R2,0,_,[]),
        format('~q.~n',[R2]).


handle_opts(Opts) :-
        opt_if_call(verbose,debug(rdf_matcher),Opts),
        opt_if_call(verbose,debug(index),Opts),
        opt_if_call(verbose,debug(rule_eval),Opts),
        opt_if_call(stacktrace,use_module(library(sparqlprog/stacktrace)),Opts),
        opt_forall(attach(X),rdf_attach_library(X),Opts),
        opt_forall(debug(X),debug(X),Opts),
        opt_forall(use(X),use_module(library(X)),Opts),
        opt_forall(use_no_import(X),use_module(library(X),[]),Opts),
        opt_forall(ontology(Prefix),set_ontology(Prefix),Opts),
        opt_forall(consult(X),consult(X),Opts),
        opt_forall(input(X),rdf_load_wrap(X),Opts),
        opt_forall(goal(X),X,Opts).


% execute a goal for every ground instance of Template
opt_forall(Template,Goal,Opts) :-
        debug(rdf_matcher,'Running ~q for all ground ~q in ~q',[Goal,Template,Opts]),
        forall((member(Template,Opts),ground(Template)),
               Goal).

opt_if_call(Opt,Goal,Opts) :-
        T =.. [Opt,Var],
        member(T,Opts),
        ground(Var),
        Var=true,
        !,
        Goal.
opt_if_call(_,_,_).

opt_if(T,Opts) :-
        member(T,Opts),
        ground(T),
        !.
opt_if(T,Opts,Opts2) :-
        select(T,Opts,Opts2),
        ground(T),
        !.

optional(G) :- G,!.
optional(_).


rdf_load_wrap(X) :-
        catch(rdf_load(X),
              _E,
              rdf_load_library(X)).

% TODO: get from elsewhere
sparqlprog_shell(Opts):-
        format('% Starting pl2sparql shell~n'),
        current_input(IO),
        HFile='.sparqlprog_history',
        (   exists_file(HFile)
        ->  rl_read_history(HFile)
        ;   true),
        repeat,
        read_line_to_codes(IO,Codes),
        (   Codes=end_of_file
        ->  !
        ;   atom_codes(A,Codes),
            rl_add_history(A),
            format('Cmd: ~w~n',[A]),
            concat_atom(L,' ',A),
            catch(run(L,Opts),
                  E,
                  (   format('ERROR:~n~w~n',[E]),fail)),
            format('SUCCESS!~n'),
            rl_write_history(HFile),
            fail).

prolog_shell(_Opts):-
        format('% Starting prolog shell~n'),
        HFile='.plhistory',
        (   exists_file(HFile)
        ->  rl_read_history(HFile)
        ;   true),
        prolog,
        format('% Bye!~n'),
        rl_write_history(HFile),
        halt.


