/**
 * This file provides dict access and modification.
 *
 * 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.
 */

/***************************************************************/
/* Pairs Access                                                */
/***************************************************************/

/**
 * dict_enum(T, P):
 * The predicate succeeds in P with the key value pairs
 * from theProlog dict T.
 */
% dict_enum(+Dict, -Pair)
dict_enum({C}, K-V) :- sys_map_member(C, K:V).

% sys_map_member(+Map, +Entry)
sys_map_member(X, X).
sys_map_member((X,_), X).
sys_map_member((_,C), X) :- sys_map_member(C, X).

/**
 * dict_pairs(S, T):
 * The predicate succeeds in T with the key value pair
 * list from the Prolog dict S.
 */
% dict_pairs(+Dict, -Pairs)
dict_pairs({}, []) :- !.
dict_pairs(H, [X|L]) :- var(H), !,
   sys_dict_singleton(X, H),
   (member(K-V, L), dict_set(H, K, V), fail; true).
dict_pairs({D}, [X|L]) :- sys_map_pairs(L, X, D).

% sys_dict_singletob(+Pair, -Dict)
sys_dict_singleton(K-V, R) :-
   R = {_}, P =.. [:, K, V],
   change_arg(1, R, P).

% sys_map_pairs(+Pairs, +Pair, -Map)
sys_map_pairs([], K-V, K:V) :- !.
sys_map_pairs([X|H], K-V, (K:V,D)) :- sys_map_pairs(H, X, D).

/**
 * dict_size(T, S):
 * The predicate succeeds in S with the number of key value
 * pairs of the Prolog dict T.
 */
% dict_size(+Dict, -Integer)
dict_size({}, N) :- !, N = 0.
dict_size({D}, N) :- sys_map_length(D, 1, N).

% sys_map_length(+Map, +Integer, -Integer)
sys_map_length((_,D), N, M) :- !, H is N+1, sys_map_length(D, H, M).
sys_map_length(_, N, N).

/***************************************************************/
/* Basic Access                                                */
/***************************************************************/

/**
 * dict_current(T, K, V):
 * The predicate succeeds in V with the value for the key K
 * in the Prolog dict T.
 */
% dict_current(+Dict, +Term, -Term)
dict_current({C}, K, V) :- sys_map_member(C, K:W), !, W = V.

/***************************************************************/
/* Backtracking Modification                                   */
/***************************************************************/

/**
 * dict_set(T, K, V, T2):
 * The predicate succeeds. It unifies T2 with the Prolog dict T
 * that is extended by the key value pair K,V if the key is new or
 * else the value for the K is replaced by V.
 */
% dict_set(+Dict, +Term, +Term, -Dict)
dict_set({}, K, V, R) :- !, R = {K:V}.
dict_set({C}, K, V, {D}) :- sys_map_replace(C, K:_, K:V, D).

% sys_map_replace(+Map, +Entry, +Entry, -Map)
sys_map_replace(X, X, Y, Y) :- !.
sys_map_replace((X,C), X, Y, (Y,C)) :- !.
sys_map_replace((Z,C), X, Y, (Z,D)) :- !, sys_map_replace(C, X, Y, D).
sys_map_replace(Z, _, Y, (Z,Y)).

/**
 * dict_add(T, K, V, T2):
 * The predicate succeeds if the key is new. It unifies T2 with
 * the Prolog dict T extended by the key value pair K,V.
 */
% dict_add(+Dict, +Term, +Term, -Dict)
dict_add({}, K, V, R) :- !, R = {K:V}.
dict_add({C}, K, V, {D}) :- sys_map_extend(C, K:_, K:V, D).

% sys_map_extend(+Map, +Entry, +Entry, -Map)
sys_map_extend(X, X, _, _) :- !, fail.
sys_map_extend((X,_), X, _, _) :- !, fail.
sys_map_extend((Z,C), X, Y, (Z,D)) :- !, sys_map_extend(C, X, Y, D).
sys_map_extend(Z, _, Y, (Z,Y)).

/**
 * dict_remove(T, K, S):
 * The predicate succeeds in S with the same Prolog dict T
 * if the key K is not present, otherwise the corresponding
 * key value pair K,V is removed.
 */
% dict_remove(+Dict, +Term, -Dict)
dict_remove({}, _, R) :- !, R = {}.
dict_remove({K:_}, K, R) :- !, R = {}.
dict_remove({C}, K, {D}) :- sys_map_delete(C, K:_, D).

% sys_map_delete(+Map, +Entry, -Map)
sys_map_delete((X,C), X, C) :- !.
sys_map_delete((Y,X), X, Y) :- !.
sys_map_delete((Y,C), X, (Y,D)) :- !, sys_map_delete(C, X, D).
sys_map_delete(Y, _, Y).

/***************************************************************/
/* Non-Backtracking Modification                               */
/***************************************************************/

/**
 * dict_set(T, K, V):
 * The predicate succeeds. As a side effect the Prolog dict T
 * is extended by the key value pair K,V if the key is new or
 * else the value for the K is replaced by V.
 */
% dict_set(+Dict, +Term, +Term)
dict_set(T, K, V) :- T = {C}, P =.. [:, K, V], sys_map_replace(C, K:_, P, 1, T).

% sys_map_replace(+Map, +Entry, +Entry, +Integer, +Compound)
sys_map_replace(X, X, Y, I, T) :- !, change_arg(I, T, Y).
sys_map_replace(T, X, Y, _, _) :- T = (X,_), !, change_arg(1, T, Y).
sys_map_replace(T, X, Y, _, _) :- T = (_,C), !, sys_map_replace(C, X, Y, 2, T).
sys_map_replace(Z, _, Y, I, T) :-
   R = (_,_), change_arg(1, R, Z),  change_arg(2, R, Y),
   change_arg(I, T, R).

/**
 * dict_add(T, K, V):
 * The predicate succeeds if the key is new. As a side effect
 * the Prolog dict T is extended by the key value pair K,V.
 */
% dict_add(+Dict, +Term, +Term)
dict_add(T, K, V) :- T = {C}, P =.. [:, K, V], sys_map_extend(C, K:_, P, 1, T).

% sys_map_extend(+Map, +Entry, +Entry, +Integer, +Compound)
sys_map_extend(X, X, _, _, _) :- !, fail.
sys_map_extend((X,_), X, _, _, _) :- !, fail.
sys_map_extend(T, X, Y, _, _) :- T = (_,C), !, sys_map_extend(C, X, Y, 2, T).
sys_map_extend(Z, _, Y, I, T) :-
   R = (_,_), change_arg(1, R, Z),  change_arg(2, R, Y),
   change_arg(I, T, R).

/**
 * dict_remove(T, K):
 * The predicate succeeds. As a side effect, the Prolog dict T
 * is unchanged, if the key K is not present, otherwise the
 * corresponding key value pair K,V is removed.
 */
% dict_remove(+Dict, +Term)
dict_remove(T, K) :- T = {C}, sys_map_delete(C, K:_, 1, T).

% sys_map_delete(+Map, +Entry, +Integer, +Compound)
sys_map_delete((X,C), X, I, T) :- !, change_arg(I, T, C).
sys_map_delete((Y,X), X, I, T) :- !, change_arg(I, T, Y).
sys_map_delete(T, X, _, _) :- T = (_,C), !, sys_map_delete(C, X, 2, T).
sys_map_delete(_, _, _, _).
