/*
** SellaNMS
** $Id: gui.cxx,v 1.19 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 "gui.h"
#include "debug.h"
#include <stdio.h>
#include <sys/stat.h>
#include <stl/string>
#include <stl/vector>

Gui::Gui(): guiQueue(NULL), guiQueueMutex(NULL), historyEnabled(false) {
	this->name.append(" gui");

	has_execute = true;
	has_scheduler = true;
}

Gui::~Gui() {
}

RESULT Gui::childStart(Module *child_module) {
	RESULT result = SUCCESS;

	result = ((Gui*) child_module)->setQueue(this->guiQueue, this->guiQueueMutex);

   return result;
}

RESULT Gui::setQueue(gui_queue_t *guiQueue, pthread_mutex_t *guiQueueMutex) {
	this->guiQueue = guiQueue;
	this->guiQueueMutex = guiQueueMutex;

	return SUCCESS;
}

RESULT Gui::scheduler() {
	RESULT result = SUCCESS;

	guiQueue = new gui_queue_t;
	guiQueueMutex = new pthread_mutex_t;

	pthread_mutex_init(guiQueueMutex, NULL);

	while (!interrupt && (result = Output::scheduler()) == SUCCESS) {
		usleep(USLEEP_LONGLOOP_TIME);
	}

   while (EBUSY == pthread_mutex_destroy(guiQueueMutex)) {
      LMSG("Gui Queue mutex is locked. Waiting for unlock.");
      usleep(USLEEP_NORMLOOP_TIME);
   }

	SAFE_DELETE(guiQueue);
	SAFE_DELETE(guiQueueMutex);

	return result;
}

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

	if (trace.thread) {
		LMSG("Gui::execute() starting");
	}

	while (!interrupt) {
		if ((result = readOutput("gui")) != SUCCESS) {
			LMSG("NOTICE: Gui failed to read output from storage.");
			result = FAILURE;
			break;
		} else {
			/* Detect if history module is registered active. */
			if (guiQueue->find("history") != guiQueue->end()) {
				historyEnabled = true;
			}

			while (!outputEmpty()) {
				/* Default policy_input. */
				polInput = polInputDefault;

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

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

						return FAILURE;
					}
				} else {
					/* FIXME: Maybe this shouldn't be done. Think about it. */
					polOutput = polInput;
				}

				processPolicyOutput(polOutput);
			}
		}

		/* Sleep the sleep of the just. */
		usleep(delay);
	}

	if (trace.thread) {
		LMSG("Gui::execute() exiting");
	}

	return result;
}

RESULT Gui::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;
		}
	}

	if (config->assignValue("delay", delay, MODULE_OUTPUT_GUI_DELAY_DEFAULT)) {
		if (delay < 1) {
			delay = MODULE_OUTPUT_GUI_DELAY_DEFAULT;
		} else {
			delay *= 1000000;
		}
	}

	/* 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_GUI_QUEUEDEPTH_DEFAULT)) {
		if (queueDepth < 1) {
			queueDepth = MODULE_OUTPUT_GUI_QUEUEDEPTH_DEFAULT;
		}
	}

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

	return result;
}

RESULT Gui::printConfig() {
	LMSG("delay: %lus", this->delay/1000000);
	LMSG("queue-depth: %lu", this->queueDepth);

	return Output::printConfig();
}

RESULT Gui::GuiQueueNext(const string &status, variable_binding_t &policy_output) {
	RESULT result = FAILURE;

	queue<variable_binding_t> *q = NULL;

	pthread_mutex_lock(guiQueueMutex);
	if (guiQueue->find(status) != guiQueue->end()) {
		q = &((*guiQueue)[status]);

		if (trace.debug) {
			LMSG("Gui::GuiQueueNext(): guiQueue[%s].size() = %lu.", status.c_str(), q->size());
		}

		if (!q->empty()) {
			result = SUCCESS;

			policy_output = q->front();
			q->pop();
		}
	} else if (trace.debug) {
		LMSG("Gui::GuiQueueNext(): guiQueue[%s] not found.", status.c_str());
	}
	pthread_mutex_unlock(guiQueueMutex);

	return result;
}

RESULT Gui::registerStatus(const string &status) {
	RESULT result = SUCCESS;

	pthread_mutex_lock(guiQueueMutex);
	if (guiQueue->find(status) == guiQueue->end()) {
		/* Register status within hash. */
		(*guiQueue)[status];
	}
	pthread_mutex_unlock(guiQueueMutex);

	return result;
}

RESULT Gui::processPolicyOutput(variable_binding_t &policy_output) {
	RESULT result = SUCCESS;
	string status;
	gui_queue_t::iterator gq_it;
	time_t t;
	static time_t next_warn = 0;

	if (policy_output.find("status") != policy_output.end()) {
		status = policy_output["status"];

		pthread_mutex_lock(guiQueueMutex);
		if ((gq_it = guiQueue->find(status)) != guiQueue->end()) {
			if (gq_it->second.size() >= queueDepth) {
				pthread_mutex_unlock(guiQueueMutex);

				t = time(NULL);
				do {
					usleep(USLEEP_LONGLOOP_TIME);
					if (time(NULL) - t % 10 == 0) { /* Complain every 10 seconds. */
						LMSG("DEBUG: GUIQueue[%s] is full (%lu). Waiting for an entry to be processed. (%lu sec)", status.c_str(), gq_it->second.size(), time(NULL) - t);
					} else if (time(NULL) - t > 300) {
						LMSG("DEBUG: GUIQueue[%s] was full (%lu) for over %lu sec. Clearing queue.", status.c_str(), gq_it->second.size(), time(NULL) - t);
						pthread_mutex_lock(guiQueueMutex);
						while (!gq_it->second.empty()) {
							gq_it->second.pop();
						}
						pthread_mutex_unlock(guiQueueMutex);
					}
				/* NOTE: If calling size() on a queue's iterator isn't thread save, then we'll have issues here. */
				} while (!interrupt && gq_it->second.size() >= queueDepth);

				pthread_mutex_lock(guiQueueMutex);
			}

			/* Insert policy_output into the appropriate queue. */
			gq_it->second.push(policy_output);

			/* Duplicate event into history if needed. */
			if (historyEnabled && policy_output.find("history") != policy_output.end() && !policy_output["history"].compare("yes")) {
				if ((*guiQueue)["history"].size() < queueDepth) {
					(*guiQueue)["history"].push(policy_output);
				} else if (time(NULL) > next_warn) {
					LMSG("DEBUG: GUIQueue[history] was full (%lu). Tail dropping entry. Suppressing warnings for 60 secs.", queueDepth);
					next_warn = time(NULL) + 60;
				}
			}
		} else {
			LMSG("NOTICE: GUI status '%s' is not registered. Data discarded.", status.c_str());
		}

		pthread_mutex_unlock(guiQueueMutex);
	}
	
	return result;
}

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