/* ***********************************************************
	key module
************************************************************ */

:- export initkey/0, keyed/3, keyof/2, val/3, valof/3.
:- export declare/1, create/2, destroy/2, set/2,key_index/3.
:- import ins/1, del/1, createpreds/2, database/1 from tr_basics1.
:- import insertbase/1, deletebase/1 from basic_funs1.
:- import length/2, append/3 from basics.

valof(N,P,L) :- val(N,P,L), !.
initkey :- dynamic(keyed(_,_,_)), dynamic(val(_,_,_)), dynamic(key(_,_)).

keyof(I,X) :- database(keyof(I,X)).

% Keys are stored in the unary predicate key(P).,
% Keys are created and destroyed using ins and del.
%
% Values of Keys are stored in the binary predicate value(P,Vlist).
% Values are changed using assert and retract.

:- op(450,xfy,[key]).
:- op(456,fx,[declare]).
:- op(456,fx,[set,create,destroy]).

% declare a key and assert the corresponding rule on table
% p(K1,K2,V1,V2) :- inserted(key(4,p(K1,K2))),
%                   value(4,p(K1,K2),[V1,V2]).
% q(K1,K2,K3) :- inserted(key(3,q(K1,K2,K3)), value(3,q(K1,K2,K3),[]).
% r(V1,V2) :- inserted(2,key(r)), value(2,r,[V1,V2]).
%
% precondition: predicate of same number of keys or same no. of arguments 
%		not declared
%
declare(key(NamenArgs,NKeys)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	insert_key_table(Name,NArgs,NKeys),
	insert_db_rule(Name,NArgs,NKeys).

check_defined(Name,NArgs,_) :-
	(
	keyed(Name,NArgs,_) -> write(Name/NArgs), 
			       write(' already declared.'), nl, !, fail
	;
	true).

% declare p/N is equivalent to declare p/N key N.
%
declare(NamenArgs) :-
	NamenArgs = Name/NKeys,
	check_defined(Name,NKeys,NKeys),
	insert_key_table(Name,NKeys,NKeys),
	insert_db_rule(Name,NKeys,NKeys).

insert_key_table(Name,NTotal,NKeys) :-
	assert(keyed(Name,NTotal,NKeys)).
insert_key_table(Name,NTotal,NKeys) :-
	retract(keyed(Name,NTotal,NKeys)), !, fail.

% insert the rule e.g p(X,Y,U,V) :- keyof(4,p(X,Y)), val(4,p(X,Y),[U,V]).
insert_db_rule(Name,NTotal,NKeys) :-
	createpreds(NKeys,KArgs),
	NVal is NTotal-NKeys,
	createpreds(NVal,VArgs),
	append(KArgs,VArgs,AllArgs),
	Lhs =.. [Name|AllArgs],
	KeyPred =.. [Name|KArgs], !,
	insertbase((Lhs :- keyof(NTotal,KeyPred), valof(NTotal,KeyPred,VArgs))).

% Create a new key, and assign it empty.
%
create(P,InitVal) :- 
	key_index(P,InitVal,Index),
	ins(keyof(Index,P)),
        insertbase(val(Index,P,InitVal)).

% Destroy a key.
%
destroy(P,Val) :- 
	key_index(P,Val,Index),
	del(keyof(Index,P)),
        deletebase(val(Index,P,Val)).

% Change the value of a predicate.
%
set(P,NewVlist) :- key_index(P,NewVlist,Index),
		   call(database(keyof(Index,P))),
                   retract(val(Index,P,OldVlist)),
                   assert(val(Index,P,NewVlist)),
                   undo_set(Index,P,OldVlist,NewVlist).

undo_set(_,_,_,_).
undo_set(Index,P,OldVlist,NewVlist) :- 
		retract(val(Index,P,NewVlist)),
                asserta(val(Index,P,OldVlist)), !, fail.

key_index(Key,Val,Index) :-
	functor(Key,_,NKey),
	length(Val,NVal),
	Index is NKey + NVal.


