/*
** SellaNMS
** $Id: status.cxx,v 1.10 2005/03/31 18:57:12 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

#ifdef HAVE_CONFIG_H
	#include "config.h"
#endif

#include "status.h"
#include "debug.h"
#include <stdio.h>
#include <sys/stat.h>
#include <stl/string>
#include <stl/vector>

Status::Status() {
	this->name.append(" status");

	has_execute = true;
	has_scheduler = false;
}

Status::~Status() {
}

RESULT Status::execute() {
	RESULT result = SUCCESS;

	if ((result = readOutput("status")) != SUCCESS) {
		LMSG("NOTICE: Status failed to read output from storage.");
		result = FAILURE;
	} else {
		while (!interrupt && !outputEmpty()) {
			/* Default policy_input. */
			polInput.clear();
			polOutput.clear();

			/* Get next policy_input */
			if (result = policyInputNext(polInput) != SUCCESS) {
				LMSG("NOTICE: Failed to obtain next policy input.");
				break;
			}

			/* Run policy on input data. */
			if (outputPolicy.evaluate(polOutput, polInput) == ACTION_FAILURE) {
				LMSG("NOTICE: Failed to evaluate output policy.");

				return FAILURE;
			}

			if ((result = processPolicyOutput(polOutput)) != SUCCESS) {
				LMSG("NOTICE: Failed to process policy output.");

				break;
			}
		}
	}

	/* Run contents of status thru output policy. */
	if ((result = processStorageContent()) != SUCCESS) {
		LMSG("NOITCE: Failed to process storage content.");

		return FAILURE;
	}

	return result;
}

RESULT Status::readConfig() {
	RESULT result = SUCCESS;

	vector<string> v;

	/* Add and compile input policy. */
	if (config->assignValues("policy input", v)) {
		LMSG("NOTICE: Input policies are not implemented yet.");
		if (inputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("NOTICE: Failed to add/compile input policies.");
			result = FAILURE;
		}
	}

	/* Add and compile output policy. */
	if (config->assignValues("policy output", v)) {
		if (outputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("NOTICE: Failed to add/compile output policies.");
			result = FAILURE;
		}
	 }

	/* Large queue-depth results in increased RAM usage, during high event periods. An overwhelmed system could grow out of control. */
		if (config->assignValue("queue-depth", queueDepth, MODULE_OUTPUT_STATUS_QUEUEDEPTH_DEFAULT)) {
			if (queueDepth < 1) {
				queueDepth = MODULE_OUTPUT_STATUS_QUEUEDEPTH_DEFAULT;
			}
	}


	/* Verify we have required policies. */
	if (outputPolicy.empty()) {
		LMSG("NOTICE: A output policy is required for this module.");
		result = FAILURE;
	}

   /* Configure Storage */
   storage.setConfig(*config);

	return result;
}

RESULT Status::printConfig() {
	LMSG("queue-depth: %lu", this->queueDepth);

	return Output::printConfig();
}

RESULT Status::processStorageContent() {
	RESULT result = SUCCESS;
	ACTION action;

	string sql;
	variable_binding_t policy_input;
	variable_binding_t policy_output;
	StorageData reader, eraser;

	if (storage.query(reader, "SELECT id, CONCAT(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timestamp)) AS age, element, interface, address, protocol, status FROM status") == SUCCESS) {
		while (reader.nextRow()) {
			reader.getULong(policy_input["id"], "id");

			reader.getString(policy_input["age"], "age");

			reader.getULong(policy_input["element-id"], "element");

			reader.getULong(policy_input["interface-id"], "interface");

			reader.getULong(policy_input["address-id"], "address");

			reader.getString(policy_input["protocol"], "protocol");

			reader.getString(policy_input["status"], "status");

			policy_input["source"] = "storage";

			/* Run policy on input reader. */
			if ((action = outputPolicy.evaluate(policy_output, policy_input)) == ACTION_FAILURE) {
				return FAILURE;
			} else if (action != ACTION_REJECT) {
				if (policy_output.find("status") != policy_output.end()) {
					if (!policy_output["status"].compare("clear")) {
						if (trace.debug) {
							LMSG("Clearing old status content for protocol '%s'. Age was %s.", policy_output["protocol"].c_str(), policy_output["age"].c_str());
						}
						
						sql = "DELETE FROM status WHERE id = " + policy_output["id"];

						if ((result = storage.query(eraser, sql.c_str())) != SUCCESS) {
							LMSG("NOTICE: Failed to delete in Status::processStorageContent().");
						}

						/* Clear for next iteration. */
						eraser.clear();
					}
				}
			}

			/* Clear for next iteration. */
			policy_output.clear();
		}
	} else {
		LMSG("NOTICE: Failed to read status data from storage.");
		result = FAILURE;
	}

	return result;
}

RESULT Status::processPolicyOutput(variable_binding_t &policy_output) {
	RESULT result = SUCCESS;

	string sql;
	StorageData writer;

	if (policy_output.find("protocol") == policy_output.end() || policy_output.find("status") == policy_output.end()) {
		LMSG("DEBUG: Status module requires a protocol and status.");
		result = FAILURE;
	} else {
		sql = "INSERT INTO status SET ";

		if (policy_output.find("element-id") != policy_output.end()) {
			if (!policy_output["element-id"].empty()) {
				sql.append("status.element = " + policy_output["element-id"] + ", ");
			}
		}

		if (policy_output.find("interface-id") != policy_output.end()) {
			if (!policy_output["interface-id"].empty()) {
				sql.append("status.interface = " + policy_output["interface-id"] + ", ");
			}
		}

		if (policy_output.find("address-id") != policy_output.end()) {
			if (!policy_output["address-id"].empty()) {
				sql.append("status.address = " + policy_output["address-id"] + ", ");
			}
		}

		sql.append("status.protocol = " + writer.quoteString(policy_output["protocol"]) + ", ");
		sql.append("status.status = " + writer.quoteString(policy_output["status"]));

		if ((result = storage.query(writer, sql.c_str())) != SUCCESS) {
			LMSG("NOTICE: Failed to write in Status:processPolicyOutput().");
		}
	}

	return result;
}

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