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

:- export initkey/0, keyed/3, key/1, val/2, create/2, size/2.
:- export declare/1, create/2, destroy/2, set/2.
:- import index_list/2 from basic_funs2.
:- import createpreds/2, ins/1, del/1 from tr_basics2.
:- import insertedname/2, deletedname/2, dbname/2,
	  insertbase/1, deletebase/1, dynpred/1 from basic_funs2.
:- import separate_key_val/4 from keys_appl2.
:- import append/3 from basics.
:- import newpred/1 from query2.
:- import length/2, append/3 from basics.
:- import retract_nr/1 from assert.

initkey :- dynamic(keyed(_,_,_)).

% 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(440,xfy,[key]).
:- op(449,xfy,[indexing]).
:- op(448,xfy,[size]).
:- 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_p(K1,K2),
%                   value_p(K1,K2,V1,V2).
% q(K1,K2,K3) :- inserted_key_q(K1,K2,K3), value_q(K1,K2,K3).
% r(V1,V2) :- inserted_key_r, value_r(V1,V2).

declare(indexing(size(key(NamenArgs,NKeys),Mem),IndexList)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	integer(Mem),
	insert_key_table(Name,NArgs,NKeys),
	insert_db_rule(Name,NArgs,NKeys,Mem,IndexList).

declare(size(key(NamenArgs,NKeys),Mem)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	integer(Mem),
	insert_key_table(Name,NArgs,NKeys),
	insert_db_rule(Name,NArgs,NKeys,Mem,[]).

declare(key(NamenArgs,NKeys)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	insert_key_table(Name,NArgs,NKeys),
	insert_db_rule(Name,NArgs,NKeys,0,[]).

% declare p/N is equivalent to declare p/N key N.
%
declare(indexing(size(NamenArgs,Mem),IndexList)) :-
	NamenArgs = Name/NArgs,
	not(NamenArgs = key(_,_)),
	check_defined(Name,NArgs,NArgs),
	integer(Mem),
	insert_key_table(Name,NArgs,NArgs),
	insert_db_rule(Name,NArgs,NArgs,Mem,IndexList).

declare(size(NamenArgs,Mem)) :-
	NamenArgs = Name/NArgs,
	not(NamenArgs = key(_,_)),
	check_defined(Name,NArgs,NArgs),
	integer(Mem),
	insert_key_table(Name,NArgs,NArgs),
	insert_db_rule(Name,NArgs,NArgs,Mem,[]).

declare(NamenArgs) :-
	not(NamenArgs = key(_,_)),
	NamenArgs = Name/NKey,
	check_defined(Name,NKey,NKey),
	insert_key_table(Name,NKey,NKey),
	insert_db_rule(Name,NKey,NKey,0,[]).

indexArg(0,[]).
indexArg(1,[1]).
indexArg(Current,[New,LastIndex|IList]) :-
	Current>1,
	Next is Current-1,
	New = Current+LastIndex,
	indexArg(Next,[LastIndex|IList]).

% return true if Name has already appeared in the key table.
check_defined(Name,NArgs,_) :-
	(
	keyed(Name,NArgs,_) -> write(Name/NArgs), 
			       write(' already declared.'), nl, !, fail
	;
	true).

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) :- db_key_p(X,Y).
insert_db_rule(Name,NKey,NKey,Mem,IndexList) :-
	name(Name,NList),
	index_list(NKey,IdList),
	append(IdList,NList,IdNList),
	append("key_",IdNList,KList),
	name(KName,KList),
	dbname(KName,DBKName),
	insertedname(KName,IName),
	deletedname(KName,DName),
	createpreds(NKey,AllArgs),
	Lhs =.. [Name|AllArgs],
	KeyPred =.. [DBKName|AllArgs], 
	DPred =.. [DName|AllArgs],
	IPred =.. [IName|AllArgs], !,
	(
	Mem = 0 -> true
	;
	Mem>0,
	(
	(IndexList = []) -> indexArg(NKey,Index)
	;
	Index = IndexList),
	index(DBKName/NKey,Index,Mem)),	
	dynpred(KeyPred),
	dynpred(IPred),
	dynpred(DPred),
	newpred(DPred),
	insertbase((Lhs :- KeyPred)),
	insertbase((KeyPred :- IPred, not(DPred))).

% insert the rule e.g p(X,Y,U,V) :- key_p(X,Y), val_p(X,Y,U,V).
insert_db_rule(Name,NArgs,NKey,Mem,IndexList) :-
	NArgs > NKey,
	name(Name,NList),
	index_list(NArgs,IdList),
	append(IdList,NList,IdNList),
	append("key_",IdNList,KList),
	name(KName,KList),
	dbname(KName,DBKName),
	insertedname(KName,IName),
	deletedname(KName,DName),
	append("val_",IdNList,VList),
	name(VName,VList),
	createpreds(NArgs,Args),
	separate_key_val(Args,NKey,Keys,_),
	Lhs =.. [Name|Args],
	KeyPred =.. [DBKName|Keys], 
	ValPred =.. [VName|Args],
	IPred =.. [IName|Keys],
	DPred =.. [DName|Keys],  !,
	(
	Mem = 0 -> true
	;
	Mem > 0,
	(
	(IndexList = []) -> indexArg(NKey,Index)
	;
	Index = IndexList),
	index(DBKName/NKey,Index,Mem),
	index(VName/NArgs,Index,Mem)),
	dynpred(ValPred),
	dynpred(KeyPred),
	dynpred(IPred),
	dynpred(DPred),
	newpred(DPred),
	insertbase((Lhs :- KeyPred, ValPred)),
	insertbase((KeyPred :- IPred, not(DPred))).

% Create a new key, and assign it to initial value
%
create(P,ValList) :-
	P =.. [Name|KArgs],
	length(KArgs,NKeys),
	length(ValList,NVal),
	NArgs is NVal + NKeys,
	keyed(Name,NArgs,NKeys),
	index_list(NArgs,IdList),
	name(Name,NList),
	append(IdList,NList,IdNList),
	append("key_",IdNList,KNList),
	name(KName,KNList),
	Key =.. [KName|KArgs], !,
	keyed(Name,NArgs,NKeys),
	ins(Key),
	(
	NArgs = NKeys  -> true
	;
	append(KArgs,ValList,VArgs),
	append("val_",IdNList,VNList),
	name(VName,VNList),
	Value =.. [VName|VArgs],
	insertbase(Value)
	).
	

% Destroy a key.
%
destroy(P,Val) :- 
	P =.. [Name|KArgs],
	length(KArgs,NKeys),
	length(Val,NVal),
	NArgs is NVal + NKeys,
	keyed(Name,NArgs,NKeys),
	index_list(NArgs,IdList),
	name(Name,NList),
	append(IdList,NList,IdNList),
	append("key_",IdNList,KNList),
	name(KName,KNList),
	Key =.. [KName|KArgs],
	del(Key),
	(
	NVal = 0  ->  true
	;
	NVal > 0,
	append(KArgs,Val,VArgs),
	append("val_",IdNList,VNList),
	name(VName,VNList),
	Value =.. [VName|VArgs],
	deletebase(Value)
	).

% Change the value of a predicate.
%
set(P,NewVlist) :- 
	P =.. [Name|KArgs],
	length(KArgs,NKeys),
	length(ValList,NVal),
	NArgs is NVal + NKeys,
	keyed(Name,NArgs,NKeys),
	index_list(NArgs,IdList),
	name(Name,NList),
	append(IdList,NList,IdNList),
	append("db_key_",IdNList,KNList),
	name(KName,KNList),
	KeyPred =.. [KName|KArgs], !,
	KeyPred,
	append("val_",IdNList,ValNList),
	name(VName,ValNList),
	createpreds(NVal,ValList),
	append(KArgs,ValList,VArgs),
	OldVPred =.. [VName|VArgs],
        retract_nr(OldVPred),
	append(KArgs,NewVlist,NewVArgs),
	NewVPred =.. [VName|NewVArgs],
        assert(NewVPred),
        undo_set(OldVPred,NewVPred).

undo_set(_,_).
undo_set(OldVPred,NewVPred) :- retract_nr(NewVPred),
                               assert(OldVPred), !, fail.

