#include "output.h"

OutputManager::OutputManager()
{
	// Set some initial values.
	ShowTimestamp = true;
	Show = false;
	MaxLines = 60;
	ConsoleFontSize = 15;
	HUDColor = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
}

OutputManager::~OutputManager() { }

VOID OutputManager::Release()
{
	// Release the console font.
	if (ConsoleFont != NULL)
	{
		ConsoleFont->Release();
		ConsoleFont = NULL;
	}

	// Release the HUD font.
	if (HUDFont != NULL)
	{
		HUDFont->Release();
		HUDFont = NULL;
	}

	// Clear the output lines.
	ConsoleLines.clear();
	HUDLines.clear();

	// Release the font handles.
	RemoveFontMemResourceEx(ConsoleFontHandle);
	RemoveFontMemResourceEx(HUDFontHandle);
}

void OutputManager::Init(IDirect3DDevice9 *device)
{
	// Get the font handles.
	ConsoleFontHandle = CreateFont(ID_CONSOLE_FONT);
	HUDFontHandle = CreateFont(ID_MAIN_FONT);

	// Create the console font.
	D3DXCreateFont(device, ConsoleFontSize, 0, 500, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLEARTYPE_NATURAL_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Consolas", &ConsoleFont);
	
	// Create the HUD font.
	D3DXCreateFont(device,40,0,500,0,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLEARTYPE_NATURAL_QUALITY,DEFAULT_PITCH || FF_DONTCARE,"Tandelle",&HUDFont);

	// Determine the maximum number of lines to show in the console.
	if (device != NULL)
		device->GetViewport(&Viewport);

	MaxLines = (int)((float)Viewport.Height / (float)ConsoleFontSize);
}

HANDLE OutputManager::CreateFont(int id)
{
	// Get the current operating instance.
	HMODULE instance = reinterpret_cast<HMODULE>(&__ImageBase);

	// Find the resource.
	HRSRC fontResource = FindResource(instance, MAKEINTRESOURCE(id), RT_FONT);
	HANDLE fontHandle;

	if (fontResource) 
	{
		 // Get the resource and apply in memory.
		 HGLOBAL mem = LoadResource(instance, fontResource);
		 void *data = LockResource(mem);
		 size_t len = SizeofResource(instance, fontResource);

		 DWORD nFonts;
		 fontHandle = AddFontMemResourceEx(data, len, NULL, &nFonts);
	}

	// Return the font handle.
	return fontHandle;
}

void OutputManager::DrawConsole()
{
	// Render each console line.
	for(int i = 0; i < ConsoleLines.size();i++)
	{
		D3DXCOLOR lineColor;
		// Set the line draw rectangle.
		SetRect(&DrawRectangle, 10, ConsoleFontSize*i, Viewport.Width, Viewport.Height);

		// Determine the color of the line from the type tag.
		switch(Type(ConsoleLines[i]))
		{
			case OUTPUT_NORMAL:
				// Normal output.
				lineColor = HUDColor;
				break;
			case OUTPUT_ERROR:
				// Error message.
				lineColor = D3DXCOLOR(0.5f, 0.0f, 0.0f, 1.0f);
				break;
			case OUTPUT_GOOD:
				// Success message.
				lineColor = D3DXCOLOR(0.1f, 0.8f, 0.2f, 1.0f);
				break;
			case OUTPUT_INFO:
				// Infomation.
				lineColor = D3DXCOLOR(0.1f, 0.2f, 0.8f, 1.0f);
				break;
			default:
				// Default.
				lineColor = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
				break;
		}

		// Remove the type tag.
		std::string ConsoleLine = ConsoleLines[i];
		ConsoleLine.erase(0, 2);

		// Draw the console line.
		ConsoleFont->DrawText(NULL, ConsoleLine.c_str(), -1, &DrawRectangle, DT_TOP, lineColor);
	}

	// If the console line list is greater than the max line count, cull some lines.
	if (ConsoleLines.size() > MaxLines)
		ConsoleLines.erase(ConsoleLines.begin() + MaxLines, ConsoleLines.begin() + ConsoleLines.size());
}

OUTPUT_TYPE OutputManager::Type(std::string str)
{
	// Parse the type from the line type tag.
	if (str[0] == '#')
	{
		if (str[1] == 'w')
			// Normal output.
			return OUTPUT_NORMAL;
		else if (str[1] == 'e')
			// Error message.
			return OUTPUT_ERROR;
		else if (str[1] == 'g')
			// Success message.
			return OUTPUT_GOOD;
		else if (str[1] == 'i')
			// Infomation.
			return OUTPUT_INFO;
		else
			// Invalid tag.
			return OUTPUT_INVALID;
	} 
	else 
	{
		return OUTPUT_INVALID;
	}
}

VOID OutputManager::SetHUDColor(D3DXCOLOR color)
{
	// Set the background color.
	HUDColor = color;
}

void OutputManager::ToHUD(std::string hudText)
{
	// Write a non-static message to the hud.
	ToHUD(hudText, false);
}

void OutputManager::ToHUD(std::string hudText, bool isStatic)
{
	// Create a new HUD message.
	HUDMessage newMessage;
	newMessage.IsStatic = isStatic;
	newMessage.Message = hudText;
	newMessage.Spawned = timeGetTime();

	newMessage.FadeIn = 250;
	newMessage.FadeOut = 2500;

	newMessage.Color = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);

	// Add the message to the list.
	HUDLines.push_back(newMessage);
}

void OutputManager::DrawHUD()
{
	for(int i = 0; i < HUDLines.size();i++)
	{
		if (!HUDLines[i].IsStatic)
		{
			// Get the current time.
			float time = (int)(timeGetTime() - (HUDLines[i].Spawned + HUDLines[i].FadeIn));

			if(time > 1.0f)
				HUDLines[i].Color.a = (time/1000.0f);

			// The time the message has been displayed for.
			float timeAlive = (float)((int)timeGetTime() - (HUDLines[i].Spawned + HUDLines[i].FadeIn + HUDLines[i].FadeOut));

			if(timeAlive > 0.0f)
			{
				HUDLines[i].Color.a = 1.0f - (timeAlive/1000.0f);

				// Remove the message.
				if (HUDLines[i].Color.a <= 0.0f)
					HUDLines.erase(HUDLines.begin()+i);
			}
		}
		else
		{
			HUDLines[i].Color.a = 1.0f;
		}
	}

	// Draw the HUD line.
	for(int i = 0; i < HUDLines.size();i++)
	{
		HUDLines[i].Color = D3DXCOLOR(HUDColor.r, HUDColor.g, HUDColor.b, HUDLines[i].Color.a);

		// Set the HUD line draw rectangle.
		SetRect(&DrawRectangle, 10.0f, Viewport.Height-(45.0f*(i+1)), Viewport.Width, Viewport.Height);
		HUDFont->DrawText(NULL, HUDLines[i].Message.c_str(), -1, &DrawRectangle, DT_TOP, HUDLines[i].Color);
	}
}

void OutputManager::NewLine() 
{
	// Turn off the timestamp.
	bool oldShowTimestamp = ShowTimestamp;
	ShowTimestamp = false;

	// Write an empty line.
	Write(OUTPUT_NORMAL, "");

	// Apply old timestamp setting.
	ShowTimestamp = oldShowTimestamp;
}

void OutputManager::Write(OUTPUT_TYPE type, char* outputContent, ...) 
{
	// Create a line buffer.
	const size_t bufferSize = 500;

	// Parse the argument list for the line.
	char tempBuffer[bufferSize];
	va_list arglist;
	
	va_start(arglist, outputContent);
		vsprintf_s(tempBuffer, outputContent, arglist);
	va_end(arglist);

	// Create a new line from the parsed buffer.
	std::string newLine = tempBuffer;
	
	// Add timestamp if enabled.
	if (ShowTimestamp)
		newLine.insert(0, GetTimestamp());

	// Apply tpye tag to determine line color.
	switch (type) 
	{
		case OUTPUT_ERROR:
			// Error message.
			newLine.insert(0, "#e");
			break;
		case OUTPUT_GOOD:
			// Success message.
			newLine.insert(0, "#g");
			break;
		case OUTPUT_INFO:
			// Information.
			newLine.insert(0, "#i");
			break;
		default:
			// Normal message.
			newLine.insert(0, "#w");
			break;
	}

	// Add the new line to the list.
	ConsoleLines.insert(ConsoleLines.begin(), newLine);
}

void OutputManager::Clear()
{
	// Clear the console output.
	ConsoleLines.clear();
}

std::string OutputManager::GetTimestamp() 
{
	// Construct a timestamp from the current system time.
	char timeStr[12];

	_strtime_s(timeStr);
	strcat_s(timeStr, " | ");

	return timeStr;
}