/*
** SellaNMS
** $Id: policy_framework.cxx,v 1.18 2005/11/25 02:57:47 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_framework.cxx,v 1.18 2005/11/25 02:57:47 sella Exp $";

#include "policy_framework.h"
#include <stdio.h>
#include <sys/timeb.h>
#include <stl/vector>

PolicyFramework::PolicyFramework() {
}

bool PolicyFramework::empty() {
	if (this->policyStatements.empty()) {
		return true;
	}
	return false;
}

/*
	Evaluate the list of policy-statements of this policy framework object against a list of variable bindings.

	Parameters
		output:	the variables that were set in the "then set" clause of the matched term(s) in the policy set of this framework object.
		input:	the variables to match against the "from" clause of each term in the policy set of this framework object.

	Return Values
		ACTION_NEXT_INPUT:		x
*/
ACTION PolicyFramework::evaluate(variable_binding_t &output, slist<variable_binding_t> &input) {
	for (slist<variable_binding_t>::iterator v = input.begin(); v != input.end(); ++v) {
		switch (this->evaluate(output, *v)) {
			case ACTION_NEXT_INPUT:
				return ACTION_NEXT_INPUT;

			case ACTION_ACCEPT:
				return ACTION_ACCEPT;

			case ACTION_REJECT:
				return ACTION_REJECT;

			case ACTION_FAILURE:
				return ACTION_FAILURE;
		}
	}
	return ACTION_NEXT_TERM;
}

/*
	Evaluate the list of policy-statements of this policy framework object against a single variable binding.

	Parameters
		output:	the variables that were set in the "then set" clause of the matched term(s) in the policy set of this framework object.
		input:	the variables to match against the "from" clause of each term in the policy set of this framework object.

	Return Values
		ACTION_NEXT_INPUT:		x
*/
ACTION PolicyFramework::evaluate(variable_binding_t &output, variable_binding_t &input) {
	output = input;

	for (slist<string>::iterator p = this->appliedPolicyStatements.begin(); p != this->appliedPolicyStatements.end(); ++p) {
		if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
			LMSG("Policy Statement: %s", p->c_str());
			if (TRACE_ISSET(TRACE_POLICY_IN)) {
				LMSG("   Type: INPUT");
			} else if (TRACE_ISSET(TRACE_POLICY_OUT)) {
				LMSG("   Type: OUTPUT");
			}
		}
		if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
			LMSG("   Input Variables {");
			for (variable_binding_t::iterator v = input.begin(); v != input.end(); ++v) {
				LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
			}
			LMSG("   }");
		}
		switch ((this->policyStatements[*p]).evaluate(output)) {
			case ACTION_NEXT_INPUT:
				if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
					LMSG("   Output Variables {");
					for (variable_binding_t::iterator v = output.begin(); v != output.end(); ++v) {
						LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
					}
					LMSG("   }");
					LMSG("   Action: %s", "Next Input");
				}
				return ACTION_NEXT_INPUT;

			case ACTION_ACCEPT:
				if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
					LMSG("   Output Variables {");
					for (variable_binding_t::iterator v = output.begin(); v != output.end(); ++v) {
						LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
					}
					LMSG("   }");
					LMSG("   Action: %s", "Accept");
				}
				return ACTION_ACCEPT;

			case ACTION_REJECT:
				if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
					LMSG("   Output Variables {");
					for (variable_binding_t::iterator v = output.begin(); v != output.end(); ++v) {
						LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
					}
					LMSG("   }");
					LMSG("   Action: %s", "Reject");
				}
				return ACTION_REJECT;

			case ACTION_FAILURE:
				if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
					LMSG("   Output Variables {");
					for (variable_binding_t::iterator v = output.begin(); v != output.end(); ++v) {
						LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
					}
					LMSG("   }");
					LMSG("   Action: %s", "Internal Failure");
				}
				return ACTION_FAILURE;
		}
		if (TRACE_ISSET(TRACE_POLICY_IN) || TRACE_ISSET(TRACE_POLICY_OUT)) {
			LMSG("   Output Variables {");
			for (variable_binding_t::iterator v = output.begin(); v != output.end(); ++v) {
				LMSG("      '%s' = '%s'", v->first.c_str(), v->second.c_str());
			}
			LMSG("   }");
			LMSG("   Action: %s", "Next Policy");
		}
	}
	return ACTION_NEXT_TERM;
}

/*
	Add a list of policy-statements to this policy framework object.

	Parameters
		config:			the section of the configuration from the level "policy-options".
		policy_names:	an ordered list of the policy-statements to append to this policy framework.

	Return Values
		SUCCESS:	if the policy-statement was successfully compiled.
		FAILURE: if the policy-statement failed to compile.
*/
RESULT PolicyFramework::addPolicies(ConfigNode *config, vector<string> &policy_names) {
	/* Loop through policy vector and add each policy. */
	for (vector<string>::iterator policy_name = policy_names.begin(); policy_name != policy_names.end(); ++policy_name) {
		if (this->addPolicy(config, *policy_name) != SUCCESS) {
			LMSG("ERROR: Failed to add policy-statement %s.", policy_name->c_str());

			return FAILURE;
		}
	}

	return SUCCESS;
}

/*
	Add a policy-statement to this policy framework object.

	Parameters
		config:			the section of the configuration from the level "policy-options".
		policy_names:	a policy-statements to append to this policy framework.

	Return Values
		SUCCESS:	if the policy-statement was successfully compiled.
		FAILURE: if the policy-statement failed to compile.
*/
RESULT PolicyFramework::addPolicy(ConfigNode *config, string &policy_name) {
	RESULT result = SUCCESS;

	ConfigNode			policy_config;
	ConfigNode			map_config;

	PolicyStatement	policy_statement;

	string				format_name;
	slist<string>		map_names;

	/* Set Storage Config (Setup conn to db) */
	if (!storage.isConfigured() && storage.setConfig(*config) != SUCCESS) {
		return FAILURE;
	}

	if (TRACE_ISSET(TRACE_POLICY_SQL)) {
		storage.setDebug(true);
	}

	if (config->assignNode("policy-options policy-statement " + policy_name, policy_config)) {
		policy_statement.setStorage(&this->storage);

		if (policy_statement.compile(&policy_config) != SUCCESS) {
			LMSG("ERROR: Failed to compile policy-statement %s.", policy_name.c_str());

			result = FAILURE;
		} else {
			if (policy_statement.getAppliedFormatStatement(format_name) != SUCCESS) {
				LMSG("ERROR: Failed to apply format-statement %s to policy-statement %s.", format_name.c_str(), policy_name.c_str());

				result = FAILURE;
			} else {
				if (config->isNode("policy-options format-statement " + format_name)) {
					if (this->addFormat(config, format_name) != SUCCESS) {
						LMSG("ERROR: Failed to add format-statement %s.", format_name.c_str());

						result = FAILURE;
					}
				} else {
					LMSG("ERROR: Format-statement %s specified in policy-options %s is not defined.", format_name.c_str(), policy_name.c_str());

					result = FAILURE;
				}
			}

#if 1
			if (policy_statement.getAppliedMapStatements(map_names) != SUCCESS) {
				LMSG("ERROR: Failed to get applied map-statements from policy-statements.");

				result = FAILURE;
			} else {
				for (slist<string>::iterator map_name = map_names.begin(); map_name != map_names.end(); ++map_name) {
					if (config->assignNode("policy-options map-statement " + *map_name, map_config)) {
						if (this->addMap(&map_config, *map_name) != SUCCESS) {
							LMSG("ERROR: Failed to add map-statement %s.", map_name->c_str());

							result = FAILURE;
						}
					} else {
						LMSG("ERROR: map-statement %s is not defined.", map_name->c_str());

						result = FAILURE;
					}
				}
			}
#endif

			if (result != FAILURE) {
				policy_statement.setName(policy_name);

				if (policy_statement.applyFormatStatement(this->formatStatements[format_name]) != SUCCESS) {
					LMSG("ERROR: Failed to apply format-statement %s to policy-statement %s.", format_name.c_str(), policy_name.c_str());

					result = FAILURE;
				} else {
					policy_statement.setCompiledStatements(&(this->policyStatements), &(this->mapStatements));

					this->policyStatements[policy_name] = policy_statement;
					this->appliedPolicyStatements.insert(this->appliedPolicyStatements.end(), policy_name);
				}
			}
		}
	} else {
		LMSG("ERROR: policy-statement %s is not defined.", policy_name.c_str());

		result = FAILURE;
	}
	
	return result;
}

/*
	Add a format-statement to this policy framework object.

	Parameters
		config:			the section of the configuration from the level "policy-options format-statement <name>".
		format_name:	a format-statement to add to this policy framework.

	Return Values
		SUCCESS:	if the format-statement was successfully added or the format-statement was already present in this policy framework.
		FAILURE: if the format-statement was unable to be added.
*/
RESULT PolicyFramework::addFormat(ConfigNode *config, string &format_name) {
	RESULT result = SUCCESS;

#if 1
	ConfigNode			format_config;
	ConfigNode			map_config;

	slist<string>		map_names;
#endif

	FormatStatement	format_statement;

	if (this->formatStatements.find(format_name) == this->formatStatements.end()) {
		if (config->assignNode("policy-options format-statement " + format_name, format_config)) {

			if (format_statement.compile(&format_config) != SUCCESS) {
				LMSG("ERROR: failed to compile format-statement %s.", format_name.c_str());

				result = FAILURE;
			} else {
				format_statement.setName(format_name);
				format_statement.setStorage(&this->storage);

				this->formatStatements[format_name] = format_statement;

#if 1
				if (format_statement.getAppliedMapStatements(map_names) != SUCCESS) {
					LMSG("ERROR: Failed to get applied map-statements from format-statements.");

					result = FAILURE;
				} else {
					for (slist<string>::iterator map_name = map_names.begin(); map_name != map_names.end(); ++map_name) {
						if (config->assignNode("policy-options map-statement " + *map_name, map_config)) {
							if (this->addMap(&map_config, *map_name) != SUCCESS) {
								LMSG("ERROR: Failed to add map-statement %s.", map_name->c_str());

								result = FAILURE;
							}
						} else {
							LMSG("ERROR: map-statement %s is not defined.", map_name->c_str());

							result = FAILURE;
						}
					}
				}
#endif
			}
		}
	}
	return result;
}

/*
	Add a map-statement to this policy framework object.

	Parameters
		config:		the section of the configuration from the level "policy-options map-statement <name>".
		map_name:	a map-statement to add to this policy framework.

	Return Values
		SUCCESS:	if the map-statement was successfully added or the map-statement was already present in this policy framework.
		FAILURE: if the map-statement was unable to be added.
*/
RESULT PolicyFramework::addMap(ConfigNode *config, string &map_name) {
	RESULT result = SUCCESS;

	MapStatement	map_statement;

	if (this->mapStatements.find(map_name) == this->mapStatements.end()) {
		/* FIXME: Hey, this is 7 lines down. I don't think this one should be here! */
		map_statement.setStorage(&this->storage);

		if (map_statement.compile(config) != SUCCESS) {
			LMSG("ERROR: failed to compile map-statement %s.", map_name.c_str());

			result = FAILURE;
		} else {
			map_statement.setName(map_name);
			map_statement.setStorage(&this->storage);

			this->mapStatements[map_name] = map_statement;
		}
	}
	return result;
}

/*
	Retrieve a listing of all variable names referenced by the policy-statements within this policy framework.

	Parameters
		referenced:	a list of variables, grouped by term, which were referenced by this policy framework.

	Return Values
		SUCCESS:	if the policy-statement was successfully compiled.
		FAILURE: if the policy-statement failed to compile.
*/
RESULT PolicyFramework::getReferencedVariables(variable_reference_t &referenced) {
	RESULT result = SUCCESS;

	/* referenced should be passed empty. Calls to getReferencedVariables() do not overwrite data. */
	assert(referenced.empty());

	for (slist<string>::iterator policy = this->appliedPolicyStatements.begin(); policy != this->appliedPolicyStatements.end(); ++policy) {
		if (this->policyStatements.find(*policy) != this->policyStatements.end()) {
			if ((this->policyStatements[*policy]).getReferencedVariables(referenced) != SUCCESS) {
				LMSG("ERROR: failed to get referenced variables for policy-statement %s.", policy->c_str());

				result = FAILURE;
			}
		} else {
			LMSG("ERROR: policy-statement %s is not compiled.", policy->c_str());

			result = FAILURE;
		}
	}
	return result;
}

/*
	Clear all information from this policy framework object.
	
	Return Values
		SUCCESS:	if the policy framework was successfully cleared.
		FAILURE: if the policy framework failed to clear.
*/
RESULT PolicyFramework::clear() {
	this->appliedPolicyStatements.clear();
	this->mapStatements.clear();
	this->policyStatements.clear();
	this->formatStatements.clear();
	
	return SUCCESS;
}

/*
	Print all information about this policy framework object.
	
	Return Values
		SUCCESS:	if the policy framework was successfully printed.
		FAILURE: if the policy framework failed to print.
*/
RESULT PolicyFramework::print(unsigned long depth) {
	RESULT result = SUCCESS;

	string	tab = "";
	for (unsigned long i = 0; i < depth; i++) {
		tab.append("   ");
	}

	LMSG("policy-options {");
	for (hash_map<string, PolicyStatement>::iterator s = this->policyStatements.begin(); s != this->policyStatements.end(); ++s) {
		s->second.print(depth + 1);
	}
	for (hash_map<string, FormatStatement>::iterator s = this->formatStatements.begin(); s != this->formatStatements.end(); ++s) {
		s->second.print(depth + 1);
	}
	for (hash_map<string, MapStatement>::iterator s = this->mapStatements.begin(); s != this->mapStatements.end(); ++s) {
		s->second.print(depth + 1);
	}
	LMSG("}");

	return result;
}

RESULT PolicyFramework::printVariableBinding(variable_binding_t &policy) {
	for (variable_binding_t::iterator it = policy.begin(); it != policy.end(); ++it) {
		LMSG("policy[%s] = %s", it->first.c_str(), it->second.c_str());
	}

	return SUCCESS;
}


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