//
// WindowsConsoleChannel.cpp
//
// $Id: //poco/1.4/Foundation/src/WindowsConsoleChannel.cpp#2 $
//
// Library: Foundation
// Package: Logging
// Module:  WindowsConsoleChannel
//
// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#include "Poco/WindowsConsoleChannel.h"
#include "Poco/Message.h"
#if defined(POCO_WIN32_UTF8)
#include "Poco/UnicodeConverter.h"
#endif
#include "Poco/String.h"
#include "Poco/Exception.h"


namespace Poco {


WindowsConsoleChannel::WindowsConsoleChannel():
	_isFile(false),
	_hConsole(INVALID_HANDLE_VALUE)
{
	_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	// check whether the console has been redirected
	DWORD mode;	
	_isFile = (GetConsoleMode(_hConsole, &mode) == 0);
}


WindowsConsoleChannel::~WindowsConsoleChannel()
{
}


void WindowsConsoleChannel::log(const Message& msg)
{
	std::string text = msg.getText();
	text += "\r\n";
	
#if defined(POCO_WIN32_UTF8)
	if (_isFile)
	{
		DWORD written;
		WriteFile(_hConsole, text.data(), static_cast<DWORD>(text.size()), &written, NULL);	
	}
	else
	{
		std::wstring utext;
		UnicodeConverter::toUTF16(text, utext);
		DWORD written;
		WriteConsoleW(_hConsole, utext.data(), static_cast<DWORD>(utext.size()), &written, NULL);
	}
#else
	DWORD written;
	WriteFile(_hConsole, text.data(), text.size(), &written, NULL);	
#endif
}


WindowsColorConsoleChannel::WindowsColorConsoleChannel():
	_enableColors(true),
	_isFile(false),
	_hConsole(INVALID_HANDLE_VALUE)
{
	_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	// check whether the console has been redirected
	DWORD mode;	
	_isFile = (GetConsoleMode(_hConsole, &mode) == 0);
	initColors();
}


WindowsColorConsoleChannel::~WindowsColorConsoleChannel()
{
}


void WindowsColorConsoleChannel::log(const Message& msg)
{
	std::string text = msg.getText();
	text += "\r\n";

	if (_enableColors && !_isFile)
	{
		WORD attr = _colors[0];
		attr &= 0xFFF0;
		attr |= _colors[msg.getPriority()];
		SetConsoleTextAttribute(_hConsole, attr);
	}

#if defined(POCO_WIN32_UTF8)
	if (_isFile)
	{
		DWORD written;
		WriteFile(_hConsole, text.data(), static_cast<DWORD>(text.size()), &written, NULL);	
	}
	else
	{
		std::wstring utext;
		UnicodeConverter::toUTF16(text, utext);
		DWORD written;
		WriteConsoleW(_hConsole, utext.data(), static_cast<DWORD>(utext.size()), &written, NULL);
	}
#else
	DWORD written;
	WriteFile(_hConsole, text.data(), text.size(), &written, NULL);	
#endif

	if (_enableColors && !_isFile)
	{
		SetConsoleTextAttribute(_hConsole, _colors[0]);
	}
}


void WindowsColorConsoleChannel::setProperty(const std::string& name, const std::string& value)
{
	if (name == "enableColors")
	{
		_enableColors = icompare(value, "true") == 0;
	}
	else if (name == "traceColor")
	{
		_colors[Message::PRIO_TRACE] = parseColor(value);
	}
	else if (name == "debugColor")
	{
		_colors[Message::PRIO_DEBUG] = parseColor(value);
	}
	else if (name == "informationColor")
	{
		_colors[Message::PRIO_INFORMATION] = parseColor(value);
	}
	else if (name == "noticeColor")
	{
		_colors[Message::PRIO_NOTICE] = parseColor(value);
	}
	else if (name == "warningColor")
	{
		_colors[Message::PRIO_WARNING] = parseColor(value);
	}
	else if (name == "errorColor")
	{
		_colors[Message::PRIO_ERROR] = parseColor(value);
	}
	else if (name == "criticalColor")
	{
		_colors[Message::PRIO_CRITICAL] = parseColor(value);
	}
	else if (name == "fatalColor")
	{
		_colors[Message::PRIO_FATAL] = parseColor(value);
	}
	else
	{
		Channel::setProperty(name, value);
	}
}


std::string WindowsColorConsoleChannel::getProperty(const std::string& name) const
{
	if (name == "enableColors")
	{
		return _enableColors ? "true" : "false";
	}
	else if (name == "traceColor")
	{
		return formatColor(_colors[Message::PRIO_TRACE]);
	}
	else if (name == "debugColor")
	{
		return formatColor(_colors[Message::PRIO_DEBUG]);
	}
	else if (name == "informationColor")
	{
		return formatColor(_colors[Message::PRIO_INFORMATION]);
	}
	else if (name == "noticeColor")
	{
		return formatColor(_colors[Message::PRIO_NOTICE]);
	}
	else if (name == "warningColor")
	{
		return formatColor(_colors[Message::PRIO_WARNING]);
	}
	else if (name == "errorColor")
	{
		return formatColor(_colors[Message::PRIO_ERROR]);
	}
	else if (name == "criticalColor")
	{
		return formatColor(_colors[Message::PRIO_CRITICAL]);
	}
	else if (name == "fatalColor")
	{
		return formatColor(_colors[Message::PRIO_FATAL]);
	}
	else
	{
		return Channel::getProperty(name);
	}
}


WORD WindowsColorConsoleChannel::parseColor(const std::string& color) const
{
	if (icompare(color, "default") == 0)
		return _colors[0];
	else if (icompare(color, "black") == 0)
		return CC_BLACK;
	else if (icompare(color, "red") == 0)
		return CC_RED;
	else if (icompare(color, "green") == 0)
		return CC_GREEN;
	else if (icompare(color, "brown") == 0)
		return CC_BROWN;
	else if (icompare(color, "blue") == 0)
		return CC_BLUE;
	else if (icompare(color, "magenta") == 0)
		return CC_MAGENTA;
	else if (icompare(color, "cyan") == 0)
		return CC_CYAN;
	else if (icompare(color, "gray") == 0)
		return CC_GRAY;
	else if (icompare(color, "darkGray") == 0)
		return CC_DARKGRAY;
	else if (icompare(color, "lightRed") == 0)
		return CC_LIGHTRED;
	else if (icompare(color, "lightGreen") == 0)
		return CC_LIGHTGREEN;
	else if (icompare(color, "yellow") == 0)
		return CC_YELLOW;
	else if (icompare(color, "lightBlue") == 0)
		return CC_LIGHTBLUE;
	else if (icompare(color, "lightMagenta") == 0)
		return CC_LIGHTMAGENTA;
	else if (icompare(color, "lightCyan") == 0)
		return CC_LIGHTCYAN;
	else if (icompare(color, "white") == 0)
		return CC_WHITE;
	else throw InvalidArgumentException("Invalid color value", color);
}


std::string WindowsColorConsoleChannel::formatColor(WORD color) const
{
	switch (color)
	{
	case CC_BLACK:        return "black";
	case CC_RED:          return "red";
	case CC_GREEN:        return "green";
	case CC_BROWN:        return "brown";
	case CC_BLUE:         return "blue";
	case CC_MAGENTA:      return "magenta";
	case CC_CYAN:         return "cyan";
	case CC_GRAY:         return "gray";
	case CC_DARKGRAY:     return "darkGray";
	case CC_LIGHTRED:     return "lightRed";
	case CC_LIGHTGREEN:   return "lightGreen";
	case CC_YELLOW:       return "yellow";
	case CC_LIGHTBLUE:    return "lightBlue";
	case CC_LIGHTMAGENTA: return "lightMagenta";
	case CC_LIGHTCYAN:    return "lightCyan";
	case CC_WHITE:        return "white";
	default:              return "invalid";
	}
}


void WindowsColorConsoleChannel::initColors()
{
	if (!_isFile)
	{
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		GetConsoleScreenBufferInfo(_hConsole, &csbi);
		_colors[0] = csbi.wAttributes;
	}
	else
	{
		_colors[0] = CC_WHITE;
	}
	_colors[Message::PRIO_FATAL]       = CC_LIGHTRED;
	_colors[Message::PRIO_CRITICAL]    = CC_LIGHTRED;
	_colors[Message::PRIO_ERROR]       = CC_LIGHTRED;
	_colors[Message::PRIO_WARNING]     = CC_YELLOW;
	_colors[Message::PRIO_NOTICE]      = _colors[0];
	_colors[Message::PRIO_INFORMATION] = _colors[0];
	_colors[Message::PRIO_DEBUG]       = CC_GRAY;
	_colors[Message::PRIO_FATAL]       = CC_GRAY;
}


} // namespace Poco
