/*
** SellaNMS
** $Id: policy_statement_then.cxx,v 1.11 2006/01/24 07:08:09 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static const char rcsid[] = "$Id: policy_statement_then.cxx,v 1.11 2006/01/24 07:08:09 sella Exp $";

#include "policy_statement_then.h"
#include "common/snmp_util.h"
#include <stdio.h>
#include <stl/string>

PolicyStatementThen::PolicyStatementThen() {
	this->compiledPolicies = NULL;
	this->compiledMaps = NULL;
}

PolicyStatementThen::PolicyStatementThen(Then const &src) {
	*this = src;
}

PolicyStatementThen::PolicyStatementThen(PolicyStatementThen const &src) {
	*this = src;
}

PolicyStatementThen::~PolicyStatementThen() {

}

Then const& PolicyStatementThen::operator=(Then const &src) {
	/* Typecast src to its real type. */
	PolicyStatementThen *real_src = (PolicyStatementThen*) &src;

	return operator=(*real_src);
}

PolicyStatementThen const& PolicyStatementThen::operator=(PolicyStatementThen const &src) {
	/* Wipe contents. */
	this->clear();

	/* Do object specifics. */
	this->action = src.action;
	this->maps = src.maps;
	this->variables = src.variables;
	this->outputs = src.outputs;
	this->policies = src.policies;
	this->counters = src.counters;	
	this->compiledPolicies = src.compiledPolicies;
	this->compiledMaps = src.compiledMaps;

	return *this;
}

RESULT PolicyStatementThen::getAppliedMapStatements(slist<string> &map_names) {
	for (slist<string>::iterator m = this->maps.begin(); m != this->maps.end(); ++m) {
		map_names.push_front(*m);
	}
	return SUCCESS;
}

RESULT PolicyStatementThen::setCompiledStatements(hash_map<string, PolicyStatement> *policies, hash_map<string, MapStatement> *maps) {
	this->compiledPolicies = policies;
	this->compiledMaps = maps;

	return SUCCESS;
}

ACTION PolicyStatementThen::evaluate(variable_binding_t &input) {
	string value;
	ACTION action;

	for (slist<string>::iterator map = this->maps.begin(); map != this->maps.end(); ++map) {
		if (this->compiledMaps->find(*map) != this->compiledMaps->end()) {
			if (((*(this->compiledMaps))[*map]).evaluate(input) == ACTION_FAILURE) {
				return ACTION_FAILURE;
			}
		}
	}
	for (variable_binding_t::iterator variable = this->variables.begin(); variable != this->variables.end(); ++variable) {
		// This is too slow -- need new data structure
		value = variable->second;
		this->replaceVariables(input, value);
		input[variable->first] = value;
	}
	for (slist<policy_call_t>::iterator policy = this->policies.begin(); policy != this->policies.end(); ++policy) {
		if (this->compiledPolicies->find(policy->policy) != this->compiledPolicies->end()) {
			for (variable_binding_t::iterator variable = policy->arguments.begin(); variable != policy->arguments.end(); ++variable) {
				// This is too slow -- need new data structure
				input[variable->first] = variable->second;
			}
#if 0
			if (!((*(this->compiledPolicies))[policy->policy]).evaluate(input) & (ACTION_NEXT_STATEMENT | ACTION_NEXT_TERM)) {
				break;
			}
#else
			action = ((*(this->compiledPolicies))[policy->policy]).evaluate(input);
			if (action & (ACTION_NEXT_STATEMENT | ACTION_NEXT_TERM)) {
				break;
			} else if (action == ACTION_FAILURE) {
				return ACTION_FAILURE;
			}
#endif
		} else {
			LMSG("Policy: failed to evaluate sub-policy call to %s", policy->policy.c_str());
		}
	}
	for (slist<PolicyStatementThenOutput>::iterator output = this->outputs.begin(); output != this->outputs.end(); ++output) {
		if (output->evaluate(input) == ACTION_FAILURE) {
			return ACTION_FAILURE;
		}
	}
	for (slist<string>::iterator counter = this->counters.begin(); counter != this->counters.end(); ++counter) {
		// FIXME: Isn't something suppoes to happen here??
	}
	return this->action;
}

RESULT PolicyStatementThen::compile(ConfigNode *config) {
	RESULT result = SUCCESS;

	ConfigNode						*sub_config = NULL;
	PolicyStatementThenOutput	output;

	string							t_var;

	string							value;
	vector<string>					values;
	vector<string>					variables;
	policy_call_t					sub_policy;

	if (config->assignValues("apply-map", values)) {
		for (vector<string>::iterator v = values.begin(); v != values.end(); ++v) {
			this->maps.insert(this->maps.end(), *v);
		}
	}
	if (config->assignValues("set", values)) {
		for (vector<string>::iterator v = values.begin(); v != values.end(); ++v) {
			t_var = *v;
			resolveMIBObject(t_var);

			if (config->assignValue("set " + *v, value)) {
				resolveMIBObject(value);

				this->variables[t_var] = value;
			} else {
				this->variables[t_var] = "";
			}
		}
	}
	if (config->assignValues("output", values)) {
		for (vector<string>::iterator v = values.begin(); v != values.end(); ++v) {
			output.clear();
			output.setName(*v);
			output.setStorage(this->storage);
			
			if (config->assignNode("output " + *v, sub_config)) {
				if (output.compile(sub_config) != SUCCESS) {
					LMSG("Policy: unable to compile output %s.", v->c_str());

					result = FAILURE;
				} else {
					this->outputs.insert(this->outputs.end(), output);
				}
				SAFE_DELETE(sub_config);
			} else {
				LMSG("Policy: no configuration available for output %s.", v->c_str());
				
				result = FAILURE;
			}
		}
	}
	if (config->assignValues("evaluate policy-statement", values)) {
		for (vector<string>::iterator p = values.begin(); p != values.end(); ++p) {
			sub_policy.policy = *p;
			sub_policy.arguments.clear();

			if(config->assignValues("evaluate policy-statement " + sub_policy.policy, variables)) {
				for (vector<string>::iterator v = variables.begin(); v != variables.end(); ++v) {
					if (config->assignValue("evaluate policy-statement " + *p + *v, value)) {
						sub_policy.arguments[*v] = value;
					} else {
						sub_policy.arguments[*v] = "";
					}
				}
				this->policies.insert(this->policies.end(), sub_policy);
			} else {
				LMSG("Policy: could not find evaluate policy-statement %s clause", sub_policy.policy.c_str());
				LMSG("FIXME: add a variable under the sub-policy call to work around this issue.");
			}
		}
	}
	if (config->assignValues("counter", values)) {
		for (vector<string>::iterator v = values.begin(); v != values.end(); ++v) {
			this->counters.insert(this->counters.end(), *v);
		}
	}
	if (config->isNode("next-term")) {
		this->action = ACTION_NEXT_TERM;
	} else if (config->isNode("next-policy")) {
		this->action = ACTION_NEXT_STATEMENT;
	} else if (config->isNode("next-input")) {
		this->action = ACTION_NEXT_INPUT;
	} else if (config->isNode("accept")) {
		this->action = ACTION_ACCEPT;
	} else if (config->isNode("reject")) {
		this->action = ACTION_REJECT;
	} else {
		this->action = ACTION_NEXT_TERM;
	}

	return result;
}

RESULT PolicyStatementThen::clear() {
	this->action = 0;
	this->maps.clear();
	this->variables.clear();
	this->outputs.clear();
	this->policies.clear();
	this->counters.clear();
	
	return SUCCESS;
}

RESULT PolicyStatementThen::print(unsigned long depth) {
	string	tab = "", str;
	for (unsigned long i = 0; i < depth; i++) {
		tab.append("   ");
	}

	LMSG("%sthen {", tab.c_str());
	
	if (!this->maps.empty()) {
		str.erase();
		for (slist<string>::iterator map = this->maps.begin(); map != this->maps.end(); ++map) {
			str.append(" " + *map);
		}
		LMSG("%s   apply-map [%s ];", tab.c_str(), str.c_str());
	}
	if (!this->variables.empty()) {
		LMSG("%s   set {", tab.c_str());
		for (variable_binding_t::iterator v = this->variables.begin(); v != this->variables.end(); ++v) {
			LMSG("%s      %s  %s;", tab.c_str(), v->first.c_str(), v->second.c_str());
		}	
		LMSG("%s   }", tab.c_str());
	}
	if (!this->outputs.empty()) {
		LMSG("%s   output {", tab.c_str());
		for (slist<PolicyStatementThenOutput>::iterator o = this->outputs.begin(); o != this->outputs.end(); ++o) {
			o->print(depth + 2);
		}	
		LMSG("%s   }", tab.c_str());
	}
	if (!this->policies.empty()) {
		LMSG("%s   evaluate {", tab.c_str());
		for (slist<policy_call_t>::iterator p = this->policies.begin(); p != this->policies.end(); ++p) {
			LMSG("%s      policy-statement %s {", tab.c_str(), p->policy.c_str());
			for (variable_binding_t::iterator v = p->arguments.begin(); v != p->arguments.end(); ++v) {
				LMSG("%s         %s  %s;", tab.c_str(), v->first.c_str(), v->second.c_str());
			}
			LMSG("%s      }", tab.c_str());
		}
		LMSG("%s   }", tab.c_str());
	}
	if (!this->counters.empty()) {
		str.erase();
		for (slist<string>::iterator c = this->counters.begin(); c != this->counters.end(); ++c) {
			str.append(" " + *c);
		}
		LMSG("%s   counter [%s ];", tab.c_str(), str.c_str());
	}
	LMSG("%s}", tab.c_str());

	return SUCCESS;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim:
** vim: noet ts=3 sw=3 sts=3
*/
