#include "Logging.h"

#include <iostream>
#include <cstdarg>
#include <cstdlib>
#include <assert.h>
#include <ctime>
#include <fstream>

// #include "EnumToString.h"

namespace Engine {

static Logging *LoggingInstance = NULL;
static LogLevel requested_level = LOG_DEFAULT_LEVEL;
static std::string requested_logfilename("");

/*
 * Inherited Module functions
 */
int Logging::OnInit (int argc, char* argv[]) {
	mPrintLevel = requested_level;

	if (requested_logfilename != "")
		SetLogFilename (requested_logfilename.c_str());

	Log (LogLevelDebug, "Logging Init");
	LoggingInstance = this;

	return 0;
}

void Logging::OnDestroy () {
	Log (LogLevelDebug, "Logging Destroy");

	if (mLogFileOut)
		mLogFileOut.close();

	LoggingInstance = NULL;
}

/*
 * Module specific functions
 */
void Logging::Log (LogLevel level, const char *str, ...) {
	static char msg[LOG_MAX_MESSAGE_LENGTH];
	static int last_length = LOG_MAX_MESSAGE_LENGTH;
	static int i;
	
	for (i = 0; i < last_length; i++)
		msg[i] = 0;

	va_list ap;
	va_start(ap, str);
	vsprintf(msg, str, ap);
	va_end(ap);

	last_length = strlen (msg);
	assert (last_length < LOG_MAX_MESSAGE_LENGTH);

	if (level >= mPrintLevel)
		std::cout << msg << std::endl;

	LogEntry log_entry (level, msg);
	mLogEntries.push_back (log_entry);

	// write the logs to file
	if (mLogFileOut) {
		/// \TODO also write out the log level
		time_t timer = time(NULL);
		std::string timestr (asctime(localtime(&timer)));
		mLogFileOut << timestr.substr(0, timestr.size() - 1) << ' ' 
			<< GetStringLogLevel(level)
			<< ": " << msg << std::endl;
		mLogFileOut.flush();
	}

	if (level == LogLevelError) {
		std::cerr << "Error occured: Aborting!" << std::endl;
		mLogFileOut << "Error occured: Aborting!" << std::endl;
		mLogFileOut.flush();
		mLogFileOut.close();
	}

	// we abort if there was an error
	assert (level != LogLevelError);
}

void Logging::SetLogPrintLevel (LogLevel print_level) {
	mPrintLevel = print_level;
}

void Logging::SetLogFilename (const std::string &filename) {
	mLogFileOut.open (filename.c_str(), std::ios_base::trunc);

	if (!mLogFileOut) {
		LogError ("Could not open logfile %s for writing!", filename.c_str());
	}

	requested_logfilename = "";
};

const LogEntry &Logging::GetLastEntry () {
	static LogEntry null_message (LogLevelMessage, "");

	if (mLogEntries.size() > 0) {
		return mLogEntries[mLogEntries.size() - 1];
	}

	return null_message;
}

/*
 * Globally visible functions 
 */
void SetLogPrintLevel (LogLevel print_level) {
	if (!LoggingInstance) {
		requested_level = print_level;
		return;
	}

	LoggingInstance->SetLogPrintLevel (print_level);
}

void SetLogFilename (const std::string &filename) {
	if (!LoggingInstance) {
		requested_logfilename = filename;
		return;
	}

	LoggingInstance->SetLogFilename (filename);
}

void LogError (const char* str, ...) {
	assert (LoggingInstance);

	static char msg[LOG_MAX_MESSAGE_LENGTH];
	static int last_length = LOG_MAX_MESSAGE_LENGTH;
	static int i;
	
	for (i = 0; i < last_length; i++)
		msg[i] = 0;

	va_list ap;
	va_start(ap, str);
	vsprintf(msg, str, ap);
	va_end(ap);

	last_length = strlen (msg);
	assert (last_length < LOG_MAX_MESSAGE_LENGTH);

	LoggingInstance->Log (LogLevelError, msg);
}

void LogWarning (const char* str, ...) {
	assert (LoggingInstance);

	static char msg[LOG_MAX_MESSAGE_LENGTH];
	static int last_length = LOG_MAX_MESSAGE_LENGTH;
	static int i;
	
	for (i = 0; i < last_length; i++)
		msg[i] = 0;

	va_list ap;
	va_start(ap, str);
	vsprintf(msg, str, ap);
	va_end(ap);

	last_length = strlen (msg);
	assert (last_length < LOG_MAX_MESSAGE_LENGTH);

	LoggingInstance->Log (LogLevelWarning, msg);
}

void LogMessage (const char* str, ...) {
	assert (LoggingInstance);

	static char msg[LOG_MAX_MESSAGE_LENGTH];
	static int last_length = LOG_MAX_MESSAGE_LENGTH;
	static int i;
	
	for (i = 0; i < last_length; i++)
		msg[i] = 0;

	va_list ap;
	va_start(ap, str);
	vsprintf(msg, str, ap);
	va_end(ap);

	last_length = strlen (msg);
	assert (last_length < LOG_MAX_MESSAGE_LENGTH);

	LoggingInstance->Log (LogLevelMessage, msg);
}

void LogDebug (const char* str, ...) {
	assert (LoggingInstance);
	static char msg[LOG_MAX_MESSAGE_LENGTH];
	static int last_length = LOG_MAX_MESSAGE_LENGTH;
	static int i;
	
	for (i = 0; i < last_length; i++)
		msg[i] = 0;

	va_list ap;
	va_start(ap, str);
	vsprintf(msg, str, ap);
	va_end(ap);

	last_length = strlen (msg);
	assert (last_length < LOG_MAX_MESSAGE_LENGTH);

	LoggingInstance->Log (LogLevelDebug, msg);
}

const LogEntry& GetLastLogEntry () {
	assert (LoggingInstance);
	return LoggingInstance->GetLastEntry ();
}

}
