/**
 * 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(library(text/charsio)).

/*******************************************************************/
/* HTTP Server                                                     */
/*******************************************************************/

/**
 * http_server_new(M):
 * The predicate succeeds in M with a new http server.
 */
% http_server_new(-Map)
http_server_new(Map) :-
   ir_object_new(Map),
   os_http_server_new(Server),
   ir_object_set(Map, 'server', Server),
   ir_object_set(Map, 'state', 0).

/**
 * http_server_on(M, T, L, G):
 * The predicate succeeds. As a side effect it adds a type T stackfull event
 * handler with formal paramaters L and callback goal G to the HTTP server M.
 **/
% http_server_on(+Map, +Atom, +List, +Goal)
http_server_on(Map, _, _, _) :-
   (ir_object_current(Map, 'state', 1); ir_object_current(Map, 'state', 2)),
   throw(error(resource_error(state_error),_)).
http_server_on(Map, Type, Paras, Goal) :-
   ir_object_current(Map, 'server', Server),
   Head =.. [''|Paras],
   sys_frost_horn((Head :- Goal), 0, Native),
   os_http_server_on(Server, Type, Native).

/**
 * http_server_listen(M, P):
 * The predicate succeeds. As a side effect the server M
 * starts listening on port P.
 */
% http_server_listen(+Map, +Integer)
http_server_listen(Map, _) :-
   (ir_object_current(Map, 'state', 1); ir_object_current(Map, 'state', 2)),
   throw(error(resource_error(state_error),_)).
http_server_listen(Map, Port) :-
   ir_object_current(Map, 'server', Server),
   sys_http_server_listen(Server, Port),
   ir_object_set(Map, 'state', 1).

% sys_http_server_listen(+Server, +Integer)
sys_http_server_listen(Server, Port) :- current_prolog_flag(state_async, on), !,
   os_http_listen_promise(Server, Port, Q),
   '$YIELD'(Q).
sys_http_server_listen(Server, Port) :-
   os_http_listen_sync(Server, Port).

/**
 * http_server_close(M):
 * The predicate succeeds. As a side effect the server M is closed.
 */
http_server_close(Map) :-
   ir_object_current(Map, 'state', 2),
   throw(error(resource_error(state_error),_)).
http_server_close(Map) :-
   ir_object_current(Map, 'state', 0), !,
   ir_object_set(Map, 'state', 2).
http_server_close(Map) :-
   ir_object_current(Map, 'server', Server),
   sys_http_server_close(Server),
   ir_object_set(Map, 'state', 2).

% sys_http_server_close(+Server)
sys_http_server_close(Server) :- current_prolog_flag(state_async, on), !,
   os_http_close_promise(Server, Q),
   '$YIELD'(Q).
sys_http_server_close(Server) :-
   os_http_close_sync(Server).

/*******************************************************************/
/* HTTP Request                                                    */
/*******************************************************************/

/**
 * url_search_params(U, P, L):
 * The predicate succeeds in P with the non-query part of U and
 * in L with the query part of U decoded into a key-value list.
 */
% url_search_params(+Atom, -Atom, -List)
url_search_params(Url, Path2, List) :-
   sub_atom(Url, Pos, _, Pos2, '?'), !,
   sub_atom(Url, 0, Pos, _, Path),
   sub_atom(Url, _, Pos2, 0, Query),
   percent_encode(Path2, Path),
   url_search_params_query(Query, List).
url_search_params(Url, Url2, []) :-
   percent_encode(Url2, Url).

% url_search_params_query(+Atom, -List)
url_search_params_query(Query, [Pair|List]) :-
   sub_atom(Query, Pos, _, Pos2, '&'), !,
   sub_atom(Query, 0, Pos, _, Component),
   sub_atom(Query, _, Pos2, 0, Rest),
   url_search_params_pair(Component, Pair),
   url_search_params_query(Rest, List).
url_search_params_query(Query, [Pair]) :-
   url_search_params_pair(Query, Pair).

% url_search_params_pair(+Atom, -Pair)
url_search_params_pair(Component, Key2-Value2) :-
   sub_atom(Component, Pos, _, Pos2, '='), !,
   sub_atom(Component, 0, Pos, _, Key),
   sub_atom(Component, _, Pos2, 0, Value),
   percent_encode(Key2, Key),
   percent_encode(Value2, Value).
url_search_params_pair(Component, Component2-'') :-
   percent_encode(Component2, Component).

/**
 * http_current_headers(S, M):
 * The predicate succeeds in M with the headers of the HTTP request S.
 */
% http_current_headers(+Request, -Map)
http_current_headers(S, M) :-
   os_http_current_headers(S, H),
   sys_header_results(H, M).

/**
 * http_input_new(S, R):
 * http_input_new(S, R, O):
 * The predicate succeeds in R with a new text reader for the
 * HTTP request S. The ternary predicate allows specifying
 * text reader options.
 */
% http_input_new(+Request, -Stream)
http_input_new(S, W) :-
   sys_http_input_new(S, 0rNone, W).

% http_input_new(+Request, -Stream, +List)
http_input_new(S, W, O) :-
   ir_object_new(Map),
   sys_stream_opts(O, Map),
   sys_http_input_new(S, Map, W).

% sys_http_input_new(+Request, +Map, -Stream)
sys_http_input_new(S, Map, R) :- current_prolog_flag(read_async, on), !,
   os_http_input_promise_opts(S, Map, R, Q),
   '$YIELD'(Q).
sys_http_input_new(S, Map, R) :-
   os_http_input_sync_opts(S, Map, R).

/*******************************************************************/
/* HTTP Response                                                   */
/*******************************************************************/

/**
 * http_write_head(S, C, H):
 * The predicate succeeds. As a side effect it writes the status
 * code C and the headers map H to the HTTP response S.
 */
% http_write_head(+Response, +Integer, +List)
http_write_head(Res, Code, List) :-
   ir_object_new(Map),
   sys_header_opts(List, Map),
   os_http_write_head(Res, Code, Map).

/**
 * http_output_new(S, W):
 * http_output_new(S, W, O):
 * The predicate succeeds in W with a new text writer for the
 * HTTP response S. The ternary predicate allows specifying
 * text writer options.
 */
% http_output_new(+Response, -Stream)
http_output_new(S, W) :-
   os_http_output_opts(S, 0rNone, W).

% http_output_new(+Response, -Stream, +List)
http_output_new(S, W, O) :-
   ir_object_new(Map),
   sys_stream_opts(O, Map),
   os_http_output_opts(S, Map, W).

/*******************************************************************/
/* Foreign Predicates                                              */
/*******************************************************************/

% os_http_server_new(F)
% defined in foreign(misc/httplib)

% os_http_server_on(F, C)
% defined in foreign(misc/httplib)

% os_http_listen_sync(F, P)
% defined in foreign(misc/httplib)

% os_http_listen_promise(F, P, Q)
% defined in foreign(misc/httplib)

% os_http_close_sync(F)
% defined in foreign(misc/httplib)

% os_http_close_promise(F, Q)
% defined in foreign(misc/httplib)

% http_current_method(F, G)
% defined in foreign(misc/httplib)

% http_current_path(F, G)
% defined in foreign(misc/httplib)

% os_http_current_headers(F, G)
% defined in foreign(misc/httplib)

% os_http_write_head(F, G, H)
% defined in foreign(misc/httplib)

% os_http_output_opts(F, G, H)
% defined in foreign(misc/httplib)

% os_http_input_promise_opts(F, G, H, I)
% defined in foreign(misc/httplib)

:- ensure_loaded(foreign(misc/httplib)).

