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

:- export initkey/0, keyed/3, size/2.
:- export declare/1, separate_key_val/4, create_and_call/1.

:- import length/2, append/3 from basics.
:- import ins/1, createpreds/2 from tr_basics4.
:- import assert/1 from assert.
:- import dynpred/1 from basic_funs4.

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

% 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
% no need to use keys but still have to check a key has one value when
% creating it.

check_defined(Name,NArgs,_) :-
	(
	keyed(Name,NArgs,_) -> write_canonical(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),
	assert(keyed(Name,NArgs, NKeys)),
	add_all_rules(Name,NArgs,NKeys,Mem,IndexList).

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

declare(key(NamenArgs,NKeys)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	assert(keyed(Name,NArgs,NKeys)),
	add_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),
	assert(keyed(Name,NArgs,NArgs)),
	add_all_rules(Name,NArgs,NArgs,Mem,IndexList).

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

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

create_and_call(X) :- 
        X =.. [Name|Args],
        length(Args,NArgs),
        keyed(Name,NArgs,NKeys),
        (
        NKeys = NArgs -> CPred = ins(X)
        ;
        NVal is NArgs-NKeys,
        separate_key_val(Args,NKeys,Keys,_),
        createpreds(NVal,OldVals),
        append(Keys,OldVals,OldArgs),
        OldPred =.. [Name|OldArgs],
        CPred = (not(OldPred), assert(X)) ),
	call(CPred).


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

add_all_rules(Name,NArgs,NKeys,Mem,IndexList) :-
	(
	Mem > 0 -> 
		(
		IndexList = [] -> indexArg(NKeys,Index)
		;
		Index = IndexList),
		index(Name/NArgs,Index,Mem)
	;
	true
	),
	dynpred(Name/NArgs).


% separate that attributes into keys and values.
%
separate_key_val(Args,0,[],Val) :- Val = 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.

not(X) :- X, !, fail
	  ;
	  true.
