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

:- export initkey/0, keyedlist/1, keyed/3, size/2.
:- export declare/1, undo_set/2, separate_key_val/4, create/1, destroy/1, set/1.
:- import append/3 from basics.
:- import retract/1, assert/1 from assert.
:- import file_name/3, index_list/2, set_name/3, create_name/3, 
	  destroy_name/3, deletedname/2, ins_name/2, del_name/2, 
	  key_name/3, val_name/3, insertedname/2
	  from naming3.
:- import createpreds/2 from tr_basics3.
:- import dynpred/1, insertbase/1, write_pred/1, insert_file/1
	  from basic_funs3.

initkey :- dynamic(keyed(_,_,_)), assert(keyedlist([])).

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

:- op(449,xfy,[indexing]).
:- op(448,xfy,[size]).
:- op(440,xfy,[key]).
:- op(456,fx,[declare,create,destroy,set]).

% 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]).

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

declare(indexing(size(key(NamenArgs,NKeys),Mem),IndexList)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	integer(Mem),
	insertbase(keyed(Name,NArgs, NKeys)),
	create_all_rules(Name,NArgs,NKeys,Mem,IndexList).
	
declare(size(key(NamenArgs,NKeys),Mem)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	integer(Mem),
	insertbase(keyed(Name,NArgs, NKeys)),
	create_all_rules(Name,NArgs,NKeys,Mem,[]).

declare(key(NamenArgs,NKeys)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	insertbase(keyed(Name,NArgs,NKeys)),
	create_all_rules(Name,NArgs,NKeys,0,[]).

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

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

declare(NamenArgs) :-
	not(NamenArgs = key(_,_)),
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NArgs),
	insertbase(keyed(Name,NArgs,NArgs)),
	create_all_rules(Name,NArgs,NArgs,0,[]).

% provide a list of index, e.g., [3+2+1,2+1,1]
indexArg(0,[]).
indexArg(1,[1]).
indexArg(Current,[New,LastIndex|IList]) :-
	Current>1,
	Next is Current-1,
	New = Current+LastIndex,
	indexArg(Next,[LastIndex|IList]).

% assert the new rules created when a new tuple is declared
create_all_rules(Name,NArgs,NKeys,Mem,IndexList) :-
	telling(OldFile),
	seeing(FileIn),
	file_name(Name,NArgs,FName),
	tell(FName),
	assert_all_rules(Name,NArgs,NKeys),
	told,
	index_and_initpred(Name,NKeys,NArgs,Mem,IndexList),
	insert_file(FName), !,
	tell(OldFile),
	see(FileIn).

index_and_initpred(Name,NKeys,NArgs,Mem,IndexList) :-
	(
	(IndexList = []) -> indexArg(NKeys,Index)
	;
	Index = IndexList ),
	name(Name,NameList),
	index_list(NArgs,IdList),
	append(IdList,NameList,IdNameList),
	append("key_",IdNameList,KList),
	append("val_",IdNameList,VList),
	append("i_key_",IdNameList,IList),
	append("d_key_",IdNameList,DList),
	name(KName,KList),
	name(VName,VList),
	name(IName,IList),
	name(DName,DList),
	(
	Mem = 0 -> true
	;
	index(KName/NKeys,Index,Mem),
	index(VName/NArgs,Index,Mem),
	index(IName/NKeys,Index,Mem),
	index(DName/NKeys,Index,Mem)
	),
	dynpred(VName/NArgs),
	dynpred(IName/NKeys),
	dynpred(DName/NKeys).

 
newkey(Name,NArgs,NKeys) :-	
	retract(keyedlist(L)),
	assert(keyedlist([(Name,NArgs,NKeys)|L])).

undo_set(_,_).
undo_set(OldVal,NewVal) :-
	retract(NewVal), assert(OldVal), !, fail.


assert_all_rules(Name,NArgs,NKeys) :-
	newkey(Name,NArgs,NKeys),
	index_list(NArgs,IdList),
	createpreds(NArgs,Args),
	create_name(Name,IdList,CName),
	destroy_name(Name,IdList,DesName),
	set_name(Name,IdList,SName),
	key_name(Name,IdList,KName),
	val_name(Name,IdList,VName),
	insertedname(KName,IKName),
	deletedname(KName,DKName),
	ins_name(KName,InsKName),
	del_name(KName,DelKName),
	NVal is NArgs - NKeys,
	separate_key_val(Args,NKeys,Keys,_),
	write_dbrule(Name,KName,VName,Args,Keys,NVal),
	write_key_rule(KName,IKName,DKName,Keys),
	write_create(CName,InsKName,VName,Keys,Args,NVal),
	write_destroy(DesName,DelKName,VName,Keys,Args,NVal),
	( NVal = 0 -> true
	;
	write_set(SName,KName,VName,Keys,Args,NVal)),
	write_ins_key(InsKName,IKName,DKName,Keys),
	write_del_key(DelKName,KName,DKName,Keys).
%	index_and_initpred(Name,NKeys,NArgs,Mem).

write_dbrule(Name,KName,VName,Args,Keys,NVal) :-
	Lhs =.. [Name|Args],
	KPred =.. [KName|Keys],
	write_pred(Lhs),
	write(' :- '),
	(
	NVal = 0 -> write_pred(KPred)
	;
	VPred =.. [VName|Args],
	write_pred(KPred),
	write(','),
	write_pred(VPred) ),
	write('.'), nl.

% writing rule : key_p(X) :- i_key_p(X), not(d_key_p(X)).
%
write_key_rule(KName,IKName,DKName,Keys) :-
	Lhs =.. [KName|Keys],
	IKPred =.. [IKName|Keys],
	DKPred =.. [DKName|Keys],
	write_pred(Lhs), write(' :- '),
	write_pred(IKPred),
	write(','),
	write_pred(not(DKPred)) ,
	write('.'), nl.

% writing rule : create_p(X) :- ins_key_p(X), insertbase(val_p(X,_)).
%
write_create(CName,InsKName,VName,Keys,Args,NVal) :-
	Lhs =.. [CName|Args],
	InsKPred =.. [InsKName|Keys],
	write_pred(Lhs), write(' :- '),
	(
	NVal = 0 -> write_pred(InsKPred)
	;
	VPred =.. [VName|Args],
	write_pred(InsKPred),
	write(','),
	write_pred(insertbase(VPred)) ),
	write('.'), nl.
	
% writing rule : destroy_p(X) :- del_key_p(X), deletebase(val_p(X,_)).
%
write_destroy(DesName,DelKName,VName,Keys,Args,NVal) :-
	Lhs =.. [DesName|Args],
	DelKPred =.. [DelKName|Keys],
	write_pred(Lhs), write(' :- '),
	(
	NVal = 0 -> write_pred(DelKPred)
	;
	VPred =.. [VName|Args],
	write_pred(DelKPred),
	write(','),
	write_pred(deletebase(VPred)) ),
	write('.'), nl.
	
% writing rule :  set_p(X,Y) :- key_p(X),
%				retract(val_p(X,Z)),
%				assert(val_p(X,Y)),
%				undo_set(val_p(X,Z),val_p(X,Y)).
%
write_set(SName,KName,VName,Keys,Args,NVal) :-
	Lhs =.. [SName|Args],
	KPred =.. [KName|Keys],
	createpreds(NVal,Dummy),
	append(Keys,Dummy,OldList),
	OldVal =.. [VName|OldList],
	NewVal =.. [VName|Args],
	write_pred(Lhs), write(' :- '),
	write_pred(KPred),
	write(','),
	write_pred(deletebase(OldVal)),
	write(','),
	write_pred(insertbase(NewVal)) ,
	write('.'), nl.

% writing rules : ins_key_p(X) :- not(i_key_p(X)),
%				  insertbase(i_key_p(X)),
%		  ins_key_p(X) :- i_key_p(X),
%		 		  deletebase(d_key_p(X)).
%
write_ins_key(InsKName,IKName,DKName,Keys) :-
	Lhs =.. [InsKName|Keys],
	IKPred =.. [IKName|Keys],
	DKPred =.. [DKName|Keys],
	write_pred(Lhs), write(' :- '),
	write_pred(not(IKPred)),
	write(','),
	write_pred(insertbase(IKPred)),
	write('.'), nl,
	write_pred(Lhs), write(' :- '),
	write_pred(IKPred),
	write(','),
	write_pred(deletebase(DKPred)),
	write('.'), nl.

% writing rule : del_key_p(X) :- key_p(X), insertbase(d_key_p(X)).
%
write_del_key(DelKName,KName,DKName,Keys) :-
	Lhs =.. [DelKName|Keys],
	KPred =.. [KName|Keys],
	DKPred =.. [DKName|Keys],
	write_pred(Lhs), write(' :- '), 
	write_pred(KPred),
	write(','),
	write_pred(insertbase(DKPred)) ,
	write('.'), nl.

% separate that attributes into keys and values.
%
separate_key_val(Args,0,[],Args).
separate_key_val([K1|Args],NKey,[K2|Keys],Val) :-
	NKey>0,
	M is NKey - 1,
	separate_key_val(Args,M,Keys,Val),
	K2 = K1.

