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

initkey :- dynpred(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,[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_canonical(Name/NArgs), 
			       write(' already declared.'), nl, !, fail
	;
	true).


declare(size(Decl,_)) :-
	declare(Decl).

declare(key(NamenArgs,NKeys)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	assert(keyed(Name,NArgs,NKeys)),
	add_all_rules(Name,NArgs).

% declare p/N is equivalent to declare p/N key N.
%
declare(NamenArgs) :-
	not(NamenArgs = key(_,_)),
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NArgs),
	assert(keyed(Name,NArgs,NArgs)),
	add_all_rules(Name,NArgs).


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


add_all_rules(Name,NArgs) :-
	createpreds(NArgs,Args),
	Fun =.. [Name|Args],
	dynpred(Fun).

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

