/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*

B.Raoult
ECMWF Oct-93

*/

#include "mars.h"
#include "lang.h"

void print_conditions(condition *c)
{
	static char *opnames[] = {
		"val","%or","%and",
		"%not","=","<",">","<>",">=","<=","%in",
	};
	value *v;

	if(c)
	{
		putchar('[');

		switch(c->op)
		{

		case t_val:
			v = (value*)c->left;
			printf("%s\n",v->name);
			break;

		case t_func:
			printf("%s(%s)\n",(char*)c->left, (char*)c->right);
			break;

		case t_not:
			printf("%%not ");
			print_conditions(c->left);
			break;

		default:
			print_conditions(c->left);
			printf(" %s ",opnames[c->op]);
			print_conditions(c->right);
			break;
		}

		putchar(']');
	}
}

static boolean bad_param;

static int compvalues(request  *r,condition *t)
{
	condition *t1 = t->left;
	condition *t2 = t->right;
	char      *name;
	const char      *par;
	char      *val;

	if(t1->op != t_val || t2->op != t_val)
		marslog(LOG_EXIT,"Bad test");

	name = ((value*)(t1->left))->name;


	par = get_value(r,name,0);

	if(par == NULL) {
		bad_param=true;
		return -1;
	}

	val = ((value*)(t2->left))->name;


	if(is_number(par) && is_number(val)){
		if(EQ(name,"DATE"))
			return mars_date_to_julian(atol(par)) - mars_date_to_julian(atol(val));
		else
			return atof(par) - atof(val);
	}
	return strcmp(par,val);
}


static int complist(request  *r,condition *t)
{
	marslog(LOG_WARN,"IN TEST NOT IMP.");
	return 0;
}

boolean old_expver_func(request* r,char *n, char *a) {

	/* TODO: Create a global option... */
	const char* c = get_value(r,"CLASS",0);
	const char* e = no_quotes(get_value(r,"EXPVER",0));
	char path[1024];
	char line[1024];
	FILE* f;

	if(a) c = a;

	if(!c) { marslog(LOG_WARN,"old_expver_func: CLASS is not defined"); return false; }
	if(!e) { marslog(LOG_WARN,"old_expver_func: EXPVER is not defined"); return false; }

	sprintf(path,"%s/etc/old_expver.%s", getenv("MARS_HOME") ? getenv("MARS_HOME") : ".", lowcase(c));

	f = fopen(path,"r");
	if(!f) {
		marslog(LOG_EXIT|LOG_PERR,"old_expver_func: cannot open %s",path);
		return false; 
	}

	while(fgets(line, sizeof(line), f))
	{
		if(strncmp(e,line,4) == 0)
			return true;
	}

	fclose(f);

	return false;
}


boolean call_func(request* c,char *f, char *a) {
/* For now, hardcoded ... */
	if(strcmp(f,"old_expver") == 0) return old_expver_func(c,f,a);
	marslog(LOG_EXIT,"Invalid function in chk [%s](%s)", f, a?a:"null");
	return false;
}

boolean condition_check(request *c,condition *t)
{
	boolean b;
	char *p,*q;

	switch (t->op)
	{
	case t_or :
		b =  condition_check(c,t->left) || condition_check(c,t->right);
		break;

	case t_and  :
		b =  condition_check(c,t->left) && condition_check(c,t->right);
		break;

	case t_not :
		b =  !condition_check(c,t->left);
		break;

	case t_val :
		b =  count_values(c,((value*)(t->left))->name) != 0;
		break;

	case t_func :
		p = (char*)t->left;
		q = (char*)t->right;
		return call_func(c, p,q);
		break;

	case t_eq :
		b =  compvalues(c,t) == 0;
		break;

	case t_lt :
		b =  compvalues(c,t) < 0;
		break;

	case t_gt :
		b =  compvalues(c,t) > 0;
		break;

	case t_ne :
		b =  compvalues(c,t) != 0;
		break;

	case t_ge :
		b =  compvalues(c,t) >= 0;
		break;

	case t_le :
		b =  compvalues(c,t) <= 0;
		break;

	case t_in :
		b = complist(c,t);
		break;


	}

	/*
	if(mars.debug)
	{
		print_conditions(t);
		putchar('\n');
		marslog(LOG_DBUG," -> %s",b?"true":"false");
	}
	*/

	return b;
}

rule *read_check_file(const char *fname)
{
	extern rule *parser_ruls;
	rule  *r;
	if(parser(fname,false) != NOERR)
	{
		free_rule(parser_ruls);
		parser_ruls = NULL;
		return NULL;
	}
	r    = parser_ruls;
	parser_ruls = NULL;
	return r;
}


void print_actions(action *a)
{

}

void print_rules(rule *r)
{
	while(r)
	{
		printf("%%if ");
		print_conditions(r->test);
		printf(" %%then\n");
		print_actions(r->doit);
		putchar('\n');
		r = r->next;
	}
}

boolean doaction(rule *c,request *r,action *a)
{
	FILE    *f   = 0;
	request *env = 0;
	char *s;
	parameter *p;
	value     *v;
	boolean   b;
	boolean   x = true;

	while(a)
	{
		switch(a->op)
		{
		case a_set:
			p = (parameter*)a->param;
			marslog(LOG_DBUG,"Setting param %s to:",
			    p->name);

			if(mars.debug)
			{
				printf("%s\n",p->values->name);
				putchar('\n');
			}

			v = p->values;
			b = false;
			while(v)
			{
				if(b) add_value(r,p->name,v->name);
				else set_value(r,p->name,v->name);
				b = true;
				v = v->next;
			}
			break;

		case a_unset:
			s = (char*)a->param;
			marslog(LOG_DBUG,"Unsetting param %s",s);
			unset_value(r,s);
			add_value(r,"_UNSET","%s",s);
			break;

		case a_warning:
			s = (char*)a->param;
			marslog(LOG_WARN,"%s",no_quotes(s));
			break;

		case a_info:
			s = (char*)a->param;
			marslog(LOG_INFO,"%s",no_quotes(s));
			break;

		case a_mail:
			env = get_environ();
			s = (char*)a->param;
			f = mail_open(mars.dhsmail,"%s",no_quotes(s));
			mail_request(f,"MARS Request:",r);
			mail_request(f,"MARS Environment:",env);
			mail_close(f);
			break;

		case a_mailuser:
			notify_user(s,r);
			break;

		case a_error:
			s = (char*)a->param;
			marslog(LOG_EROR,"%s",no_quotes(s));
			x = false;
			break;

		case a_exit:
			s = (char*)a->param;
			marslog(LOG_EXIT,"%s",no_quotes(s));
			break;

		case a_fail:
			x = false;
			marslog(LOG_DBUG,"Fail");
			if(mars.debug)
			{
				print_one_request(r);
				putchar('\n');
				print_conditions(c->test);
				putchar('\n');
			}
			break;
		}
		a = a->next;
	}

	return x;
}

boolean check_one_request(rule *c,request *r)
{
	boolean b = true;

	if(r == NULL) return true;

	set_value(r,"_VERB",r->name);
	if(mars.appl)
		set_value(r,"_APPL",mars.appl);

	while(c)
	{
		bad_param = false;

		if(condition_check(r,c->test) && !bad_param)
			b = doaction(c,r,c->doit) && b;
		c = c->next;
	}


	return b;
}

rule *new_rule(condition *c,action *a)
{
	rule *r = NEW_CLEAR(rule);
	r->test = c;
	r->doit = a;
	return r;
}

condition *new_condition(testop op,condition *l,condition *r)
{
	condition *c = NEW_CLEAR(condition);
	c->op    = op;
	c->left  = l;
	c->right = r;
	return c;
}

action *new_action(actop op,void *parm)
{
	action *a = NEW_CLEAR(action);
	a->op    = op;
	a->param = parm;
	return a;
}

condition *clone_condition(condition *c)
{
	if(!c) return NULL;

	if(c->op == t_func)
		return new_condition(t_func,
			(condition*)strcache((char*)c->left),
			(condition*)strcache((char*)c->right));
	else if(c->op == t_val)
		return new_condition(t_val,
			(condition*)clone_all_values((value*)(c->left)),
		    NULL);
	else
		return new_condition(c->op,
		    clone_condition(c->left),
		    clone_condition(c->right)
		    );
}

void free_action(action *a)
{
	while(a)
	{
		action *b = a->next;

		switch(a->op)
		{

		case a_set:
			free_all_parameters((parameter*)a->param);
			break;

		case a_unset:
		case a_warning:
		case a_info:
		case a_mail:
		case a_mailuser:
		case a_error:
		case a_exit:
			strfree((char*)a->param);
			break;

		case a_fail:
			break;

		default:
			marslog(LOG_EXIT,"free_action : unknown action");
			break;

		}
		FREE(a);
		a = b;
	}

}

void free_condition(condition *c)
{
	if(c)
	{
		if(c->op == t_func)
		{
			strfree((char*)c->left);
			strfree((char*)c->right);
		}
		else if(c->op == t_val)
			free_all_values((value*)(c->left));
		else
		{
			free_condition(c->left);
			free_condition(c->right);
		}
		FREE(c);
	}
}

void free_rule(rule *r)
{
	while(r)
	{
		rule *n = r->next;
		free_condition(r->test);
		free_action(r->doit);
		FREE(r);
		r = n;
	}
}
