#include <string.h>
#include <assert.h>
#include <memory.h>

#include "darray.h"
#include "action.h"
#include "data.h"
#include "rule.h"
#include "ruleP.h"

AlRule
AlRule_create(predicate, actions, created, updated, name)
AlRuleMember	predicate;
Darray		actions;
time_t		created,
		updated;
char		*name;
{
	AlRule	rule;

	assert(predicate);
 	assert(actions);
	
	rule = (AlRule)Memory_allocate(sizeof(AlRuleSt));
	rule->predicate = predicate;
	rule->actions = actions;
	rule->state = (r_state)0;
	if(name != (char *)NULL)
	{	rule->name = (char *)Memory_allocate(strlen(name) + 1);
		strcpy(rule->name, name);
	}
	else
		rule->name = (char *)NULL;
	if(created)
	{	rule->creation = created;
		rule->update = updated;
	}
	else
	{	rule->update = time(&rule->creation);
		SET(rule->state, RST_NEW);
		AlData_rule_created(rule);
	}

	return rule;
}

AlRule
AlRule_copy(rule)
AlRule	rule;
{
	Darray		actions;
	unsigned int	i,
			len;

	assert(rule !=  (AlRule)NULL);

	actions = Darray_create();
	Darray_hint(actions, (unsigned)0, len = Darray_len(rule->actions));
	for(i = 0; i < len; i++)
		Darray_addh(actions, 
			    (VOIDP)AlAction_copy((AlAction)
						Darray_get(rule->actions, i)));
	return AlRule_create(AlRuleMember_copy(rule->predicate), actions,
			     rule->creation, rule->update, rule->name);
}

NORET
AlRule_destroy(rule)
AlRule	rule;
{
	unsigned	len,
			i = 0;
	
	assert(rule);
	for(len = Darray_len(rule->actions); i < len; i++)
		AlAction_destroy((AlAction)Darray_get(rule->actions, i));
	Darray_destroy(rule->actions);
	AlRuleMember_destroy(rule->predicate);
	AlData_rule_deleted(rule);
	if(rule->name)
		Memory_free((VOIDP)rule->name);
	Memory_free((VOIDP)rule);
}

Darray
AlRule_get_actions(rule)
AlRule	rule;
{
	assert(rule);
	return rule->actions;
}

time_t
AlRule_get_creation(rule)
AlRule	rule;
{
	assert(rule);
	return rule->creation;
}

time_t
AlRule_get_update(rule)
AlRule	rule;
{
	assert(rule);
	return rule->update;
}

char *
AlRule_get_name(rule)
AlRule	rule;
{
	assert(rule);
	return rule->name;
}

char *
AlRule_set_name(rule, name)
AlRule	rule;
char	*name;
{
	assert(rule);

	if(rule->name)
		Memory_free((VOIDP)rule->name);
	rule->name = (char *)Memory_allocate(strlen(name) + 1);
	return strcpy(rule->name, name);
}

NORET
AlRule_mark_changed(rule)
AlRule	rule;
{
	time_t	t;

	assert(rule);
	SET(rule->state, RST_CHG);
	t = rule->update;
	time(&rule->update);
	AlData_rule_changed(rule, t);
}

NORET
AlRule_mark_unchanged(rule)
AlRule	rule;
{
	assert(rule);
	UNSET(rule->state, RST_CHG);
}

AlRuleMember
AlRule_get_predicate(rule)
AlRule	rule;
{
	assert(rule);
	return rule->predicate;
}

AlRuleMember
AlRuleMember_mcreate_assoc(type)
AlRAType	type;
{
	AlRuleMember	rm;

	rm = (AlRuleMember)Memory_allocate(sizeof(AlRuleMemberSt));
	rm->m_kind = ALRMT_ASSOC;
	rm->type.assoc = (AlRuleAssoc)Memory_allocate(sizeof(AlRuleAssocSt));
	rm->type.assoc->a_type = type;
	rm->container = (AlRuleAssoc)NULL;
	rm->type.assoc->contents = rm->sibling = rm->previous = 
	  (AlRuleMember)NULL;
	rm->type.assoc->as_member = rm;

	return rm;
}

AlRuleMember
AlRuleMember_mcreate_field(kind, key, type, value, op)
AlRFType	kind;
char		*key,
		*type,
		*value,
		*op;
{
	AlRuleMember	rm;

	rm = (AlRuleMember)Memory_allocate(sizeof(AlRuleMemberSt));
	rm->m_kind = ALRMT_FIELD;
	rm->type.field = (AlRuleField)Memory_allocate(sizeof(AlRuleFieldSt));
	rm->type.field->f_type = kind;	
	rm->type.field->value = (char *)Memory_allocate(strlen(value) + 1);
	strcpy(rm->type.field->value, value);
	rm->type.field->op_name = (char *)Memory_allocate(strlen(op) + 1);
	strcpy(rm->type.field->op_name, op);
	rm->type.field->fc = AlFieldCore_create(type);
	AlFieldCore_set_key(rm->type.field->fc, key);
	if(AlFieldCore_parse(rm->type.field->fc, value) == Bool_FALSE)
		/* set errcode !!!! */
		return (AlRuleMember)NULL;
	rm->type.field->op_func = AlFieldType_get_field_op_proc(
				 	AlFieldCore_get_field_type(rm->type.field->fc),
					op);
	rm->type.field->as_member = rm;
	rm->container = (AlRuleAssoc)NULL;
	rm->sibling  = (AlRuleMember)NULL;
	rm->previous  = (AlRuleMember)NULL;

	return rm;
}


/* This function allows the creatrion of a field when the field type will */
 /* not parse the value of the field with the current operator.  In this */
 /* case the actual operator function is set to NULL to tell runrules not to */
 /* fire this field. */
AlRuleMember
AlRuleMember_mcreate_field_no_parse(kind, key, type, value, op)
AlRFType	kind;
char		*key,
		*type,
		*value,
		*op;
{
	AlRuleMember	rm;

	rm = (AlRuleMember)Memory_allocate(sizeof(AlRuleMemberSt));
	rm->m_kind = ALRMT_FIELD;
	rm->type.field = (AlRuleField)Memory_allocate(sizeof(AlRuleFieldSt));
	rm->type.field->f_type = kind;	
	rm->type.field->value = (char *)Memory_allocate(strlen(value) + 1);
	strcpy(rm->type.field->value, value);
	rm->type.field->op_name = (char *)Memory_allocate(strlen(op) + 1);
	strcpy(rm->type.field->op_name, op);
	rm->type.field->fc = AlFieldCore_create(type);
	AlFieldCore_set_key(rm->type.field->fc, key);
	if(AlFieldCore_parse(rm->type.field->fc, value) == Bool_FALSE)
	  rm->type.field->op_func = NULL;
	else
	  rm->type.field->op_func = 
	    AlFieldType_get_field_op_proc(
					  AlFieldCore_get_field_type(rm->type.field->fc),
					  op);
	rm->type.field->as_member = rm;
	rm->container = (AlRuleAssoc)NULL;
	rm->sibling  = (AlRuleMember)NULL;
	rm->previous  = (AlRuleMember)NULL;

	return rm;
}


Bool
AlRuleField_test_contents(field)
AlRuleField field;
{
	if(AlFieldCore_parse(field->fc,field->value) == Bool_FALSE)
             /* set errcode !!!! */
        	return Bool_FALSE;
	else
		return Bool_TRUE;
}


AlRuleMember
AlRuleMember_copy(rm)
AlRuleMember	rm;
{
	AlRuleMember	new_rm;
	AlRuleField	rf;
	AlRuleAssoc	ra,
			new_ra;
	Darray		contents;
	unsigned int	len;

	assert(rm != (AlRuleMember)NULL);

	if(rm->m_kind == ALRMT_FIELD)
	{	rf = rm->type.field;
		new_rm = AlRuleMember_mcreate_field(rf->f_type, 
				AlFieldCore_get_key(rf->fc),
				AlFieldCore_get_field_type_name(rf->fc),
				rf->value, rf->op_name);
		return new_rm;
	}
	ra = rm->type.assoc;
	new_rm = AlRuleMember_mcreate_assoc(ra->a_type);
	new_ra = new_rm->type.assoc;
	contents = Darray_create();
	AlRuleAssoc_get_contents(ra, contents);
	len = Darray_len(contents);
	while(len--)
		AlRuleAssoc_make_member_of(new_ra, AlRuleMember_copy((AlRuleMember)
					     	     Darray_reml(contents)));
	Darray_destroy(contents);
	return new_rm;
}

						    
	

NORET
AlRuleMember_destroy(rm)
AlRuleMember	rm;
{
	assert(rm);
	if(rm->m_kind == ALRMT_ASSOC)
		destroy_assoc(rm->type.assoc);
	else
		destroy_field(rm->type.field);
	Memory_free((VOIDP)rm);
}

destroy_assoc(assoc)
AlRuleAssoc	assoc;
{
	Darray	contents = Darray_create();
	unsigned	i = 0,
			len;

	AlRuleAssoc_get_contents(assoc, contents);
	for(len = Darray_len(contents); i < len; i++)
		AlRuleMember_destroy((AlRuleMember)Darray_get(contents, i));
	Darray_destroy(contents);
	Memory_free((VOIDP)assoc);
}

destroy_field(field)
AlRuleField	field;
{
	assert(field);
	Memory_free((VOIDP)field->value);
	Memory_free((VOIDP)field->op_name);
	AlFieldCore_delete(field->fc);
	Memory_free((VOIDP)field);
}

NORET
AlRuleMember_insert_after(after_here, member)
AlRuleMember	after_here,
		member;
{
        member->previous = after_here;
	member->sibling = after_here->sibling;
	after_here->sibling = member;
	if (member->sibling!=NULL)
	  member->sibling->previous = member;
	member->container = after_here->container;
}


NORET	/* Maybe should return something (like the member before it) ? */
AlRuleAssoc_make_member_of(assoc, member)
AlRuleAssoc	assoc;
AlRuleMember	member;
{
	AlRuleMember	mtmp;

	if(!assoc->contents)
	{	assoc->contents = member;
		member->container = assoc;
		return;
	}
	for(mtmp = assoc->contents; mtmp->sibling; mtmp = mtmp->sibling)
			;
	mtmp->sibling = member;
	member->container = mtmp->container;
	member->previous = mtmp;
	/* Might want to add something in reverse */
}

AlRuleMember
AlRuleAssoc_as_member(assoc)
AlRuleAssoc	assoc;
{
	assert(assoc);
	return assoc->as_member;
}

AlRuleAssoc
AlRuleMember_remove(member)
AlRuleMember	member;
{
	AlRuleMember	mtmp;
	AlRuleAssoc	assoc;

	if((assoc = member->container) == NULL)
		return (AlRuleAssoc)NULL;	/* top level assoc */
	if((mtmp = assoc->contents) == member)
	{	assoc->contents = member->sibling;
		if (member->sibling!=NULL)
		  member->sibling->previous = NULL;
		member->container = (AlRuleAssoc)NULL;
		return assoc;
	}
	else for(; mtmp->sibling != member; mtmp = mtmp->sibling)
		;
	mtmp->sibling = member->sibling;
	if (member->sibling!=NULL)
	  member->sibling->previous=mtmp;
	member->sibling = (AlRuleMember)NULL;
	member->previous = (AlRuleMember)NULL;
	member->container = (AlRuleAssoc)NULL;
	return assoc;
}

AlRMType
AlRuleMember_get_type(member)
AlRuleMember	member;
{
	assert(member);
	return	member->m_kind;
}

AlRAType
AlRuleAssoc_get_type(assoc)
AlRuleAssoc	assoc;
{
	assert(assoc);
	return assoc->a_type;
}

const char *
AlRuleAssoc_atype(at)
AlRAType	at;
{
	switch(at) {
	case ALRAT_AND:
		return "and";
	case ALRAT_OR:
		return "or";
	case ALRAT_NOT:
		return "not";
	}
}

AlRFType
AlRuleField_get_type(field)
AlRuleField	field;
{
	assert(field);
	return field->f_type;
}

NORET
AlRuleField_set_key(field, key)
AlRuleField	field;
char		*key;
{
	assert(field);

	AlFieldCore_set_key(field->fc, key);
}

Bool
AlRuleField_set_value(field, value)
AlRuleField	field;
char		*value;
{
	assert(field);

	Memory_free((VOIDP)field->value);
	field->value = (char *)Memory_allocate(strlen(value) + 1);
	strcpy(field->value, value);
	return AlFieldCore_parse(field->fc, field->value);
}

Bool
AlRuleField_set_type(field, type)
AlRuleField	field;
char		*type;
{
	assert(field);

	AlFieldCore_set_field_type(field->fc, AlFieldType_get_field_type(type));
	return AlFieldCore_parse(field->fc, field->value);
}

Bool
AlRuleField_set_op(field, op)
AlRuleField	field;
char		*op;
{
	assert(field);

	Memory_free((VOIDP)field->op_name);
	field->op_name = (char *)Memory_allocate(strlen(op) + 1);
	field->op_func = AlFieldType_get_field_op_proc(
				 	AlFieldCore_get_field_type(field->fc),
					strcpy(field->op_name, op));
	return AlFieldCore_parse(field->fc, field->value);
}

AlRuleMember
AlRuleField_as_member(field)
AlRuleField	field;
{
	assert(field);
	return field->as_member;
}

AlRuleAssoc
AlRuleMember_get_assoc(member)
AlRuleMember	member;
{
	assert(member);
	assert(member->m_kind == ALRMT_ASSOC);
	return member->type.assoc;
}

AlRuleField
AlRuleMember_get_field(member)
AlRuleMember	member;
{
	assert(member);
	assert(member->m_kind == ALRMT_FIELD);
	return member->type.field;
}

/* I don't  think this is really what you want ... !!! */
Darray
AlRuleAssoc_get_contents(assoc, contents)
AlRuleAssoc	assoc;
Darray		contents;
{
	AlRuleMember	mtmp;

	for(mtmp = assoc->contents; mtmp; mtmp = mtmp->sibling)
		Darray_addh(contents, (VOIDP)mtmp);

	return contents;
}

AlRuleMember
AlRuleMember_get_sibling(member)
AlRuleMember	member;
{
	assert(member);
	return member->sibling;
}

AlRuleMember
AlRuleMember_get_previous(member)
AlRuleMember	member;
{
	assert(member);
	return member->previous;
}

AlRuleMember
AlRuleAssoc_get_first_member(assoc)
AlRuleAssoc	assoc;
{
	assert(assoc);
	return assoc->contents;
}

AlFieldCore
AlRuleField_get_fc(field)
AlRuleField	field;
{
	assert(field);
	return field->fc;
}

char *
AlRuleField_get_strvalue(field)
AlRuleField	field;
{
	assert(field);
	return field->value;
}

char *
AlRuleField_get_op_name(field)
AlRuleField	field;
{
	assert(field);
	return field->op_name;
}

FieldOperatorProc *
AlRuleField_get_op_func(field)
AlRuleField	field;
{
	assert(field);
	return field->op_func;
}

AlRuleAssoc
AlRuleMember_get_container(member)
AlRuleMember member;
{
  return(member->container);
}
