/*
** SellaNMS
** $Id: output.cxx,v 1.39 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 "output.h"
#include "debug.h"
#include "storagedata.h"
#include <stl/slist>

Output::Output(): queueDepth(MODULE_OUTPUT_QUEUEDEPTH_DEFAULT) {
	this->name.append(" output");
	
	has_execute = true;
	has_scheduler = true;
}

Output::~Output() {
}

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

	char buf[512];
	time_t now, next;

	StorageData data;

	/* Remove stale data from output, gui_event and status. This should only be called by Output. */
	if (clearStaleData() != SUCCESS) {
		interrupt = true;

		return FAILURE;
	}

	/* Set now and next equal, so it always delete from output at least once. */
	now = next = time(NULL);
	do {
		if (now >= next) {
			if (trace.debug) {
				LMSG("deleting content older than %lus from output", age_max);
			}

			if (storage.queryf(data, "DELETE FROM output WHERE UNIX_TIMESTAMP() - %d > UNIX_TIMESTAMP(timestamp) ", age_max) != SUCCESS) {
				LMSG("NOTICE: Failed to delete from output.");
				interrupt = true;

				return FAILURE;
			}

			/* Move to next start time. */
			next = now + age_interval;
		} else {
			usleep(USLEEP_LONGLOOP_TIME);
		}
		now = time(NULL);
	} while (schedulerRunning() && !interrupt);

	return result;
}

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

	if (config->assignValue("age-maximum", age_max, MODULE_OUTPUT_AGEMAX_DEFAULT)) {
		if (age_max == 0) {
			LMSG("NOTICE: Output entry aging disabled. This isn't recommended.");
		}
	}

	config->assignValue("age-interval", age_interval, MODULE_OUTPUT_AGEINTERVAL_DEFAULT);

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

	return result;
}

bool Output::outputEmpty() const {
	return output.empty();
}

RESULT Output::handleTraceOptions() {
	/* Handle trace-options */
	if (trace.sql)
		storage.setDebug();

	if (trace.policy_sql) {
		inputPolicy.setDebug();
		outputPolicy.setDebug();
	}

	Module::handleTraceOptions();

	if (trace.policy_in && !inputPolicy.empty()) {
		if (trace.config) {
			inputPolicy.print();
		}
		inputPolicy.setTraceIn(true);
	}

	if (trace.policy_out && !outputPolicy.empty()) {
		if (trace.config) {
			outputPolicy.print();
		}
		outputPolicy.setTraceOut(true);
	}

	return SUCCESS;
}

RESULT Output::readOutput(const string &module, bool auto_delete) {
	RESULT result = SUCCESS;

	output_t out;
	string str;
	StorageData data;
	slist<unsigned long> output_ids;

	if (storage.queryf(data, "SELECT id, timestamp, UNIX_TIMESTAMP(timestamp) AS unix_timestamp, element, interface, address, module, protocol, channel, status, brief, detail FROM output WHERE module = '%s' ORDER BY timestamp LIMIT %lu", module.c_str(), queueDepth) == SUCCESS) {
		while (data.nextRow()) {
			data.getULong(out.id, "id");

			//No idea what libdbi thinks the type should be. Its angry about every type.
			data.getAuto(str, "unix_timestamp");
			out.unix_timestamp = (time_t) atol(str.c_str());

			data.getLong(out.element, "element");

			data.getLong(out.interface, "interface");

			data.getLong(out.address, "address");

			data.getString(out.module, "module");

			data.getString(out.protocol, "protocol");

			data.getString(out.channel, "channel");

			data.getString(out.status, "status");

			data.getString(out.brief, "brief");

			data.getBinary(out.detail, "detail");

			output.push_front(out);

			if (auto_delete) {
				output_ids.push_front(out.id);
			}
		}
		result = SUCCESS;
	} else {
		LMSG("NOTICE: Failed to read output data from storage.");
		result = FAILURE;
	}

	if (auto_delete && !output_ids.empty()) {
		result = deleteOutput(output_ids);
	}

	return result;
}

RESULT Output::deleteOutput(slist<unsigned long> &output_ids) {
	RESULT result = SUCCESS;

	string sql;
	char buf[132];
	StorageData data;

	if (!output_ids.empty()) {
		/* Build DELETE string. */
		sql = "DELETE FROM output WHERE ";
		for (slist<unsigned long>::iterator it = output_ids.begin(); it != output_ids.end(); ++it) {
			snprintf(buf, 132, "id = %lu OR ", *it);
			sql.append(buf);
		}
		/* Remove the last ' OR ' */
		sql.erase(sql.size() - 4);

		if (storage.query(data, sql.c_str()) != SUCCESS) {
			LMSG("NOTICE: Failed to delete output data from storage.");
			result = FAILURE;
		}
	} else {
		LMSG("NOTICE: deleteOutput called with no input. Why??");
		return FAILURE;
	}

	return result;
}

RESULT Output::clearStaleData() {
	RESULT result = SUCCESS;
	StorageData data;

	if (trace.debug) {
		LMSG("Clearing stale (all) data from gui_event, output, and status.");
	}

	if ((result = this->storage.query(data, "DELETE FROM gui_event")) != SUCCESS) {
		LMSG("WARNING: Failed to delete stale data from table gui_event");
	} else if ((result = this->storage.query(data, "DELETE FROM output")) != SUCCESS) {
		LMSG("WARNING: Failed to delete stale data from table output");
	} else if ((result = this->storage.query(data, "DELETE FROM status")) != SUCCESS) {
		LMSG("WARNING: Failed to delete stale data from table status");
	}

	return result;
}

RESULT Output::policyInputAll(slist<variable_binding_t> &policy_input_list) {
	RESULT result = FAILURE;
	variable_binding_t policy_input;

	while (!output.empty()) {
		if ((result = policyInputNext(policy_input)) == SUCCESS) {
			policy_input_list.push_front(policy_input);
			policy_input_list.reverse();
		} else {
			break;
		}
	}

	return result;
}

RESULT Output::policyInputNext(variable_binding_t &policy_input) {
	RESULT result = FAILURE;
	char buf[256];
	output_t out;

	tm ts;

	if (!output.empty()) {
		result = SUCCESS;

		out = output.front();
		output.pop_front();

		/* Element */
		if (out.element.compare("0")) {
			policy_input["element-id"] = out.element;
		}

		/* Interface */
		if (out.interface.compare("0")) {
			policy_input["interface-id"] = out.interface;
		}

		/* IPAddress */
		if (out.address.compare("0")) {
			policy_input["address-id"] = out.address;
		}

		/* Timestamp */
//		policy_input["timestamp"] = out.timestamp;

		/* Module */
		policy_input["module"] = out.module;

		/* Protocol */
		policy_input["protocol"] = out.protocol;

		/* Channel */
		policy_input["channel"] = out.channel;

		/* Status */
		policy_input["status"] = out.status;

		/* Brief */
		policy_input["brief"] = out.brief;

		/* Detail */
		policy_input["detail"] = out.detail;

		/* Setup the time. */
		localtime_r(&out.unix_timestamp, &ts);

		/* Date/Time */
		strftime(buf, 256, "%m/%d/%Y %H:%M:%S", &ts);
		policy_input["date-time"] = buf;

		/* Date */
		strftime(buf, 256, "%m/%d/%Y", &ts);
		policy_input["date"] = buf;

		/* Day of Week */
		strftime(buf, 256, "%a", &ts);
		policy_input["day-of-week"] = buf;

		/* Begin and End Time */
		strftime(buf, 256, "%H:%M", &ts);
		policy_input["begin-time"] = buf;
		policy_input["end-time"] = buf;

		/* Queue Size */
		snprintf(buf, 256, "%lu", output.size() + 1);
		policy_input["queue-size"] = buf;
	}

	return result;
}

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