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

#include "data.h"
#include "ruleset.h"
#include "rulesetP.h"
#include "memory.h"

static Rules
Rules_create(rule)
AlRule	rule;
{
	Rules	r;

	r = (Rules)Memory_allocate(sizeof(RulesSt));
	r->rule = rule;
	r->next = r->prev = (Rules)NULL;

	return r;
}

static Rules
Rules_insert_rule(rlst0, rlst1, rule)
Rules	rlst0,
	rlst1;
AlRule	rule;
{
	Rules	r;

	r = Rules_create(rule);
	r->next = rlst1;
	if(rlst0)
		rlst0->next = r;
	r->prev = rlst0;
	if(rlst1)
		rlst1->prev = r;

	return r;
}

AlRuleSet
AlRuleSet_create(desc, int_name, create, update, rules)
const char	*int_name,
		*desc;
time_t		create,
		update;
Darray		rules;
{
	AlRuleSet	rs;
	Rules		rls;
	unsigned	num_rules,
			c = 0;

	rs = (AlRuleSet)Memory_allocate(sizeof(AlRuleSetSt));
	rs->desc = (char *)Memory_allocate(strlen(desc) + 1);
	strcpy(rs->desc, desc ? desc : "");
	rs->int_name = (char *)Memory_allocate(strlen(int_name) + 1);
	strcpy(rs->int_name, int_name);
	rs->state = (rs_st)0;
	SET(rs->state, RSST_SEQ);
	rs->fst_rules = rs->lst_rules = (Rules)NULL;
	rs->rule_count = 0;
	if(rules)
		for(num_rules = Darray_len(rules); c < num_rules; c++)
		{	rls = Rules_create((AlRule)Darray_get(rules, c));
			if(!rs->fst_rules)
				rs->fst_rules = rls;
			rls->prev = rs->lst_rules;
			if(rs->lst_rules)
				rs->lst_rules->next = rls;
			rs->lst_rules = rls;
			++rs->rule_count;
		}
	if(create)
	{	rs->creation = create;
		rs->update = update;
	}
	else
	{	rs->update = time(&rs->creation);
		SET(rs->state, RSST_NEW | RSST_MOD);
		AlData_ruleset_created(rs);
	}
		
	return rs;
}

NORET
AlRuleSet_destroy(rs)
AlRuleSet	rs;
{
	Rules	r,
		nr;

	assert(rs);
	for(r = rs->fst_rules; r; r = nr)
	{	AlRule_destroy(r->rule);
		nr = r->next;
		Memory_free((VOIDP)r);
	}
	AlData_ruleset_deleted(rs);
	Memory_free((VOIDP)rs->desc);
	Memory_free((VOIDP)rs->int_name);
	Memory_free((VOIDP)rs);
}

NORET
AlRuleSet_set_ctime(rs, create)
AlRuleSet	rs;
time_t		create;
{
	assert(rs);
	rs->creation = create;
}

NORET
AlRuleSet_set_utime(rs, update)
AlRuleSet	rs;
time_t		update;
{
	assert(rs);
	rs->update = update;
}

NORET
AlRuleSet_mark_unmod(rs)
AlRuleSet	rs;
{
	assert(rs);
	UNSET(rs->state, RSST_MOD);
}

const char *
AlRuleSet_get_desc(rs)
AlRuleSet	rs;
{
	assert(rs);
	return rs->desc;
}

NORET
AlRuleSet_set_desc(rs, desc)
AlRuleSet	rs;
const char	*desc;
{
	assert(rs);
	if(rs->desc)
		Memory_free(rs->desc);
	rs->desc = (char *)Memory_allocate(strlen(desc) + 1);
	strcpy(rs->desc, desc);
}

const char *
AlRuleSet_get_internal_name(rs)
AlRuleSet	rs;
{
	assert(rs);
	return rs->int_name;
}

unsigned
AlRuleSet_number_of_rules(rs)
AlRuleSet	rs;
{
	assert(rs);
	return rs->rule_count;
}

unsigned
AlRuleSet_add_rule_to_end(rs, rule)
AlRuleSet	rs;
AlRule		rule;
{
	time_t	t;

	assert(rs);
	assert(rule);

	rs->lst_rules = Rules_insert_rule(rs->lst_rules, (Rules)NULL, rule);
 /* probably shouldn't need this special case */
	if(rs->rule_count == (unsigned)0)
		rs->fst_rules = rs->lst_rules;
	SET(rs->state, RSST_MOD);
	t = rs->update;
	time(&rs->update);
	AlData_ruleset_changed(rs, '+', rule, t);
	return ++rs->rule_count;
}

unsigned
AlRuleSet_put_rule_at_index(rs, rule, i)
AlRuleSet	rs;
AlRule		rule;
unsigned	i;
{
	Rules		rl,
			new;
	unsigned	c = 0;
	time_t		t;

	assert(rs);
	assert(rule);

 /* special case, should write procedure to do right thing instead */
	if(i == 0 && rs->rule_count == 0)
	{	rs->fst_rules = rs->lst_rules = Rules_create(rule);
		return ++rs->rule_count;
	}
	assert(i < rs->rule_count);

	for(rl = rs->fst_rules; c < i; c++, rl = rl->next)
		;
	new = Rules_insert_rule(rl->prev, rl, rule);
	if(i == 0)
		rs->fst_rules = new;
	SET(rs->state, RSST_MOD);
	t = rs->update;
	time(&rs->update);
	AlData_ruleset_changed(rs, '+', rule, t);
	return ++rs->rule_count;
}

AlRule
AlRuleSet_remove_rule(rs, index)
AlRuleSet	rs;
unsigned	index;
{
	Rules		rstmp;
	unsigned	i = 0;
	AlRule		r;
	time_t		t;

	assert(rs);
	assert(index < rs->rule_count);

	for(rstmp = rs->fst_rules; i < index; i++, rstmp = rstmp->next)
		;
	if(rstmp->next)
		rstmp->next->prev = rstmp->prev;
	else
		rs->lst_rules = rstmp->prev;
	if(rstmp->prev)
		rstmp->prev->next = rstmp->next;
	else
		rs->fst_rules = rstmp->next;
	rstmp->next = rstmp->prev = (Rules)NULL;
	SET(rs->state, RSST_MOD);
	t = rs->update;
	time(&rs->update);
	--rs->rule_count;
	AlData_ruleset_changed(rs, '-', rstmp->rule, t);

	r = rstmp->rule;
	Memory_free(rstmp);
	return r;
}

Bool
AlRuleSet_modp(rs)
AlRuleSet	rs;
{
	assert(rs);
	return TEST(rs->state, RSST_MOD) ? Bool_TRUE : Bool_FALSE;
}

Bool
AlRuleSet_delp(rs)
AlRuleSet	rs;
{
	assert(rs);
	return TEST(rs->state, RSST_DEL) ? Bool_TRUE : Bool_FALSE;
}

Bool
AlRuleSet_newp(rs)
AlRuleSet	rs;
{
	assert(rs);
	return TEST(rs->state, RSST_NEW) ? Bool_TRUE : Bool_FALSE;
}

Bool
AlRuleSet_seqp(rs)
AlRuleSet	rs;
{
	assert(rs);
	return TEST(rs->state, RSST_SEQ) ? Bool_TRUE : Bool_FALSE;
}

time_t
AlRuleSet_get_create(rs)
AlRuleSet	rs;
{
	assert(rs);
	return rs->creation;
}

time_t
AlRuleSet_get_update(rs)
AlRuleSet	rs;
{
	assert(rs);
	return rs->update;
}

AlRule
AlRuleSet_get_rule(rs, index)
AlRuleSet	rs;
unsigned	index;
{
	Rules		rstmp;
	unsigned	i = 0;

	assert(rs);
	assert(index < rs->rule_count);

	for(rstmp = rs->fst_rules; i < index; i++, rstmp = rstmp->next)
		;
	return rstmp->rule;
}

NORET
AlRuleSet_get_rules(rs, da)
AlRuleSet	rs;
Darray		da;
{
	Rules		rstmp;

	assert(rs);
	assert(da);

	Darray_hint(da, (unsigned)0, rs->rule_count);
	for(rstmp = rs->fst_rules; rstmp; rstmp = rstmp->next)
		Darray_addh(da, (VOIDP)rstmp->rule);
}

Bool
AlRuleSet_deletable_on_diskp(rs)
AlRuleSet	rs;
{
	assert(rs);
	return Bool_TRUE;
}
