/*
   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 "sys/consoleinterface.h"

namespace sys
{

const size_t DEFAULT_LOGSIZE = 2048;

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

bool con_ansicolor = false;

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(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;
			
		} 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 {
				std::cout << *c;
			}
			
		} else {
			std::cout << *c;
		}
		
		c++;
	}
}

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

int ConsoleBuffer::overflow(int c)
{
	if (c == Traits::eof())
		return Traits::not_eof(c);
		
	if (c == '\n') {
		if (ConsoleInterface::instance()) {
			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