/**
 * Modern Albufeira Prolog Interpreter
 *
 * Warranty & Liability
 * To the extent permitted by applicable law and unless explicitly
 * otherwise agreed upon, XLOG Technologies AG makes no warranties
 * regarding the provided information. XLOG Technologies AG assumes
 * no liability that any problems might be solved with the information
 * provided by XLOG Technologies AG.
 *
 * Rights & License
 * All industrial property rights regarding the information - copyright
 * and patent rights in particular - are the sole property of XLOG
 * Technologies AG. If the company was not the originator of some
 * excerpts, XLOG Technologies AG has at least obtained the right to
 * reproduce, change and translate the information.
 *
 * Reproduction is restricted to the whole unaltered document. Reproduction
 * of the information is only allowed for non-commercial uses. Selling,
 * giving away or letting of the execution of the library is prohibited.
 * The library can be distributed as part of your applications and libraries
 * for execution provided this comment remains unchanged.
 *
 * Restrictions
 * Only to be distributed with programs that add significant and primary
 * functionality to the library. Not to be distributed with additional
 * software intended to replace any components of the library.
 *
 * Trademarks
 * Jekejeke is a registered trademark of XLOG Technologies AG.
 */

:- ensure_loaded('../../transpiler/cross/cross.p').
:- ensure_loaded('transpiler.p').
:- ensure_loaded('bundler.p').

% run
run :-
   current_prolog_flag(base_url, X),
   absolute_file_name('../../', Y, [relative_to(X)]),
   set_prolog_flag(base_url, Y),
   sys_transpile_doge_core,
   sys_bundle_doge_core.

/****************************************************************/
/* Transpile & Bundle JavaScript Core                           */
/****************************************************************/

% sys_transpile_doge_core
sys_transpile_doge_core :-
   (dirty_path('transpiler/cross/trans.p', 'player/drawer/nova/bootload.mjs');
    dirty_path('transpiler/cross/encode.p', 'player/drawer/nova/bootload.mjs');
    dirty_path('transpiler/cross/cross.p', 'player/drawer/nova/bootload.mjs');
    dirty_path('player/cross/transpiler.p', 'player/drawer/nova/bootload.mjs');

    dirty_path('transpiler/drawer/loader.p', 'player/drawer/nova/bootload.mjs');
    dirty_path('transpiler/drawer/core.p', 'player/drawer/nova/bootload.mjs');
    dirty_path('transpiler/drawer/dynamic.p', 'player/drawer/nova/bootload.mjs');

    dirty_path('transpiler/drawer/code.p', 'player/drawer/nova/streams.mjs');
    dirty_path('transpiler/drawer/scanner.p', 'player/drawer/nova/streams.mjs');
    dirty_path('transpiler/drawer/term.p', 'player/drawer/nova/streams.mjs')), !,

   transpile_begin('player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_add('transpiler/cross/trans.p', 'player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_add('transpiler/cross/encode.p', 'player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_add('transpiler/drawer/loader.p', 'player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_add('transpiler/drawer/core.p', 'player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_add('transpiler/drawer/dynamic.p', 'player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   transpile_end('player/drawer/nova/bootload.mjs', [defer_meta(true)]),
   copy_time('transpiler/cross/trans.p', 'player/drawer/nova/bootload.mjs'),
   copy_time('transpiler/cross/encode.p', 'player/drawer/nova/bootload.mjs', [update(true)]),
   copy_time('transpiler/cross/cross.p', 'player/drawer/nova/bootload.mjs', [update(true)]),
   copy_time('player/cross/transpiler.p', 'player/drawer/nova/bootload.mjs', [update(true)]),
   copy_time('transpiler/drawer/loader.p', 'player/drawer/nova/bootload.mjs', [update(true)]),
   copy_time('transpiler/drawer/core.p', 'player/drawer/nova/bootload.mjs', [update(true)]),
   copy_time('transpiler/drawer/dynamic.p', 'player/drawer/nova/bootload.mjs', [update(true)]),

   transpile_begin('player/drawer/nova/streams.mjs'),
   transpile_add('transpiler/drawer/code.p', 'player/drawer/nova/streams.mjs'),
   transpile_add('transpiler/drawer/scanner.p', 'player/drawer/nova/streams.mjs'),
   transpile_add('transpiler/drawer/term.p', 'player/drawer/nova/streams.mjs'),
   transpile_end('player/drawer/nova/streams.mjs'),
   copy_time('transpiler/drawer/code.p', 'player/drawer/nova/streams.mjs'),
   copy_time('transpiler/drawer/scanner.p', 'player/drawer/nova/streams.mjs', [update(true)]),
   copy_time('transpiler/drawer/term.p', 'player/drawer/nova/streams.mjs', [update(true)]).
sys_transpile_doge_core.

% sys_bundle_doge_core
sys_bundle_doge_core :-
   (dirty_path('player/drawer/nova/store.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/machine.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/unicode.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/special.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/runtime.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/eval.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/theatre.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/bootload.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/streams.mjs', 'player/canned/nova/core.mjs');
    dirty_path('player/drawer/nova/core.mjs', 'player/canned/nova/core.mjs')), !,

   bundle_begin('player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/store.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/machine.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/unicode.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/special.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/runtime.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/eval.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/theatre.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/bootload.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/streams.mjs', 'player/canned/nova/core.mjs'),
   bundle_add('player/drawer/nova/core.mjs', 'player/canned/nova/core.mjs', [keep_export(true)]),

   copy_time('player/drawer/nova/store.mjs', 'player/canned/nova/core.mjs'),
   copy_time('player/drawer/nova/machine.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/unicode.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/special.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/runtime.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/eval.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/theatre.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/bootload.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/streams.mjs', 'player/canned/nova/core.mjs', [update(true)]),
   copy_time('player/drawer/nova/core.mjs', 'player/canned/nova/core.mjs', [update(true)]).
sys_bundle_doge_core.

/*******************************************************************/
/* Transpile Begin                                                 */
/*******************************************************************/

/**
 * transpile_begin(B):
 * transpile_begin(B, O):
 * The predicate succeeds in writing an empty JavaScript file B.
 */
% transpile_begin(+Atom)
transpile_begin(OutName) :-
   transpile_begin(OutName, []).

% transpile_begin(+Atom, +List)
transpile_begin(OutName, Opts) :-
   decode_transpile_opts(Opts, v(0,''), v(Flags,Doge)),
   copy_text('player/cross/header.mjs', OutName),
   setup_once_cleanup(
      open(OutName, append, OutStream),
      sys_transpile_prologue(OutStream, Flags, Doge),
      close(OutStream)).

% sys_transpile_prologue(+Stream, +Integer, +Atom)
sys_transpile_prologue(OutStream, Flags, Doge) :-
    (Flags /\ 2 =\= 0 ->
        cross_clear;
        true),
    (Flags /\ 1 =\= 0 ->
        write(OutStream, 'import {add, Clause, Goal, run, pred_link,\n'),
        write(OutStream, '        Cache, make_defined, Compound} from "'),
        write(OutStream, Doge),
        write(OutStream, '";\n'),
        write(OutStream, 'export function main() {\n')
    ;   true).

/*******************************************************************/
/* Transpile Add                                                   */
/*******************************************************************/

/**
 * transpile_add(A, B):
 * transpile_add(A, B, O):
 * The predicate succeeds in cross compiling the Prolog text file A
 * into the JavaScript file B.
 */
% transpile_add(+Atom, +Atom)
transpile_add(InName, OutName) :-
   transpile_add(InName, OutName, []).

% transpile_add(+Atom, +Atom, +List)
transpile_add(InName, OutName, Opts) :-
   decode_transpile_opts(Opts, v(0,''), v(Flags,_)),
   retractall(cross_flags(_)),
   assertz(cross_flags(Flags)),
   setup_once_cleanup(
      open(OutName, append, OutStream),
      sys_transpile_loading(InName, OutStream),
      close(OutStream)).

% sys_transpile_loading(+Atom, +Stream)
sys_transpile_loading(InName, OutStream) :-
   setup_once_cleanup(
      cross_loading_begin(InName, LastPred),
      sys_transpile_file(InName, OutStream),
      cross_loading_end(LastPred)).

% sys_transpile_file(+Atom, +Stream)
sys_transpile_file(InName, OutStream) :-
   setup_once_cleanup(
      open(InName, read, InStream),
      sys_transpile_stream(InName, InStream, OutStream),
      close(InStream)).

% sys_transpile_stream(+Atom, +Stream, +Stream)
sys_transpile_stream(InName, InStream, OutStream) :-
   current_task(Task),
   setup_once_cleanup(
      asserta(sys_including(InName, Task, InStream)),
      sys_transpile_lines(InStream, OutStream),
      once(retract(sys_including(InName, Task, InStream)))).

% sys_transpile_lines(+Stream, +Stream)
sys_transpile_lines(InStream, OutStream) :-
   repeat,
   read_term(InStream, Term, [variable_names(Vars), singletons(Sings)]),
   (Term = end_of_file -> !;
      sys_multiton_keys(Vars, Sings, Keys),
      sys_multiton_check(Keys),
      sys_singleton_keys(Sings, Keys2),
      sys_singleton_check(Keys2),
      expand_term(Term, Term2),
      sys_transpile_clump(Term2, OutStream),
      fail).

/****************************************************************/
/* Transpile Clump                                              */
/****************************************************************/

% sys_transpile_clump(+Term, +Stream)
sys_transpile_clump(T, OutStream) :- nonvar(T), T = [Term|Rest], !,
   sys_transpile_clump(Term, OutStream),
   sys_transpile_clump(Rest, OutStream).
sys_transpile_clump(T, _) :- nonvar(T), T = [], !.
sys_transpile_clump(T, OutStream) :-
   cross_style_static(T, Usage),
   sys_transpile_static([Usage|T], OutStream).

% sys_transpile_static(+Term, +Stream)
sys_transpile_static(T, OutStream) :- nonvar(T), T = [Clause|Rest], !,
   sys_transpile_static(Clause, OutStream),
   sys_transpile_static(Rest, OutStream).
sys_transpile_static(T, _) :- nonvar(T), T = [], !.
sys_transpile_static(T, OutStream) :-
   \+ \+ make_print_static(T, OutStream).

/*******************************************************************/
/* Transpile End                                                   */
/*******************************************************************/

/**
 * transpile_end(B):
 * transpile_end(B, O):
 * The predicate succeeds adding an epilogue to the JavaScript file B.
 */
% transpile_end(+Atom)
transpile_end(OutName) :-
   transpile_end(OutName, []).

% transpile_end(+Atom, +List)
transpile_end(OutName, Opts) :-
   decode_transpile_opts(Opts, v(0,''), v(Flags,_)),
   setup_once_cleanup(
      open(OutName, append, OutStream),
      sys_transpile_fini(OutStream, Flags),
      close(OutStream)).

% sys_transpile_fini(+Stream, +Integer)
sys_transpile_fini(OutStream, Flags) :-
   (Flags /\ 2 =\= 0 ->
       cross_defered(Term3),
       sys_transpile_static(Term3, OutStream);
       true),
   fail.
sys_transpile_fini(_, _) :-
   true.

/*******************************************************************/
/* Decode Transpile Options                                        */
/*******************************************************************/

% decode_transpile_opts(+List, +Pair, -Pair)
decode_transpile_opts(V, _, _) :- var(V),
   throw(error(instantiation_error,_)).
decode_transpile_opts([X|L], I, O) :- !,
   decode_transpile_opt(X, I, H),
   decode_transpile_opts(L, H, O).
decode_transpile_opts([], H, H) :- !.
decode_transpile_opts(L, _, _) :-
   throw(error(type_error(list,L),_)).

% decode_transpile_opt(+Option, +Pair, -Pair)
decode_transpile_opt(V, _, _) :- var(V),
   throw(error(instantiation_error,_)).
decode_transpile_opt(defer_meta(B), v(F,Y), v(G,Y)) :- !,
   sys_opt_boolean(B, 2, F, G).
decode_transpile_opt(doge(A),v(X,_), v(X,A)) :- !.
decode_transpile_opt(O, _, _) :-
   throw(error(type_error(transpile_option,O),_)).

/*******************************************************************/
/* Initialization                                                  */
/*******************************************************************/

:- initialization(run).
