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

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),_),_)) :-
	NamenArgs = Name/NArgs,
	check_defined(Name,NArgs,NKeys),
	assert(keyed(Name,NArgs, NKeys)),
	add_all_rules(Name,NArgs).

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

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 0.
%
declare(indexing(size(NamenArgs,_),_)) :-
	NamenArgs = Name/NArgs,
	not(NamenArgs = key(_,_)),
	check_defined(Name,NArgs,NArgs),
	assert(keyed(Name,NArgs,NArgs)),
	add_all_rules(Name,NArgs).

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

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


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

