/*
   sys/consoleinterface.cc
   This file is part of the Osirion project and is distributed under
   the terms of the GNU General Public License version 2
*/

#include <iomanip>
#include "sys/consoleinterface.h"

namespace sys
{

const size_t DEFAULT_LOGSIZE = 2048;

/* -- ANSI color code support -------------------------------------- */

bool con_ansicolor = false;

bool con_timestamps = false;

bool beginofline = true;

bool console_timestamps()
{
	return con_timestamps;
}

void set_console_timestamps(const bool timestamps)
{
	con_timestamps = timestamps;
}

bool ansi()
{
	return con_ansicolor;
}

void set_ansi(const bool ansi)
{
	con_ansicolor = ansi;
	if (con_ansicolor) {
		// ANSI default color
		std::cout << "\033[0;39m";
	}
}

void fallback_print_timestamp()
{
	int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, milliseconds = 0;
	get_localtime(year, month, day, hour, min, sec, milliseconds);
	std::cout << year << "-" << month << "-" << day << " " << hour << ":" << min << ":" << sec << " ";
}

void fallback_print(const std::string &text)
{
	bool is_color_code = false;
	int ansi_bold = 0;
	int ansi_color = 39;

	const char *c = text.c_str();
	while (*c) {

		if ((*c) == '\n') {
			std::cout << std::endl;
			beginofline = true;

		} else if ((*c) == '^') {

			is_color_code = true;
			ansi_bold = 0;
			ansi_color = 39;

			switch (*(c + 1)) {
				case '0': // black
					ansi_color = 0;
					ansi_bold = 1;
					break;
				case '1': // red
					ansi_color = 31;
					break;
				case '2': // green
					ansi_color = 32;
					break;
				case '3': // yellow
					ansi_color = 1;
					ansi_color = 33;
					break;
				case '4': // blue
					ansi_color = 34;
					break;
				case '5': // cyan
					ansi_color = 36;
					break;
				case '6': // magenta
					ansi_color = 35;
					break;
				case '7': // white is mapped to foreground color
					ansi_bold = 1;
					ansi_color = 39;
					break;

				case 'N': // normal
					ansi_bold = 0;
					ansi_color = 39;
					break;
				case 'B': // bold
					ansi_bold = 1;
					ansi_color = 39;
					break;
				case 'D': // debug
					ansi_bold = 0;
					ansi_color = 39;
					break;
				case 'R': // error
					ansi_bold = 0;
					ansi_color = 31;
					break;
				case 'W': // warning
					ansi_bold = 1;
					ansi_color = 33;
					break;
				case 'F': // fancy
					ansi_bold = 0;
					ansi_color = 32;
					break;
				default:
					is_color_code = false;
			}

			if (is_color_code) {
				if (con_ansicolor)
					std::cout <<  "\033[" << ansi_bold << ";" << ansi_color << "m";
				c++;
			} else {
				if (beginofline && con_timestamps) {
					fallback_print_timestamp();
				}
				std::cout << *c;
				beginofline = false;
			}

		} else {
			if (beginofline && con_timestamps) {
				fallback_print_timestamp();
			}			
			std::cout << *c;
			beginofline = false;
		}

		c++;
	}
}

/* -- ConsoleBuffer ------------------------------------------------ */

int ConsoleBuffer::overflow(int c)
{
	if (c == Traits::eof())
		return Traits::not_eof(c);

	if (c == '\n') {	
		if (ConsoleInterface::instance()) {
			/*
			 * In the rcon case, the remote console will handle the timestamps
			 */
			if (con_timestamps && !ConsoleInterface::instance()->rcon()) {
				std::ostringstream str;
				int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, milliseconds = 0;
				get_localtime(year, month, day, hour, min, sec, milliseconds);
				
				str << "^B"
					<< std::setw(4) << std::setfill('0') << year << "-" 
					<< std::setw(2) << std::setfill('0') << month << "-" 
					<< std::setw(2) << std::setfill('0') << day << " " 
					<< std::setw(2) << hour << ":" 
					<< std::setw(2) << min << ":" 
					<< std::setw(2) << sec << " " 
					<< std::setw(2) << con_buffer;
					
				ConsoleInterface::instance()->event_text(str.str());
			} else {
				ConsoleInterface::instance()->event_text(con_buffer);
			}
		} else {
			fallback_print(con_buffer);
			std::cout << std::endl;
		}
		con_buffer.clear();
	} else {
		con_buffer += c;
	}
	return c;
}

/* -- ConsoleStream ------------------------------------------------ */

ConsoleStream con_out;

ConsoleStream::ConsoleStream() : std::basic_ostream<char, Traits>(&con_buffer)
{
	clear();
}

ConsoleStream::~ConsoleStream()
{
	if (con_ansicolor) {
		// ANSI default color
		std::cout << "\033[0;39m" << std::endl;
	}
}

/* -- Console ------------------------------------------------------ */

ConsoleInterface *ConsoleInterface::consoleinterface_instance = 0;

ConsoleInterface::ConsoleInterface()
{
	if (consoleinterface_instance) {
		std::cerr << "multiple sys::ConsoleInterface instances!" << std::endl;
		sys::quit(2);
	}

	consoleinterface_rcon = false;
	consoleinterface_instance = this;
	consoleinterface_logsize = DEFAULT_LOGSIZE;
}

ConsoleInterface::~ConsoleInterface()
{
	consoleinterface_instance = 0;
}

ConsoleInterface *ConsoleInterface::instance()
{
	return consoleinterface_instance;
}


void ConsoleInterface::set_rcon(bool enable)
{
	consoleinterface_rcon = enable;
}

void ConsoleInterface::set_logsize(const size_t logsize)
{
	consoleinterface_logsize = logsize < 16 ? 16 : logsize;
}

void ConsoleInterface::event_text(const std::string & text)
{
	if (rcon()) {
		consoleinterface_rconbuf.push_back(text);
	} else {
		while (consoleinterface_log.size() >= consoleinterface_logsize) {
			consoleinterface_log.pop_front();
		}

		consoleinterface_log.push_back(text);
		print(text);
	}
}

void ConsoleInterface::print(const std::string & text)
{
	fallback_print(text);
	std::cout << std::endl;
}

} // namespace sys