#include "stdafx.h"

#include "Gui/ConLog.h"
#include <wx/listctrl.h>
#include "Ini.h"
#include <fstream>
#include <vector>

LogWriter ConLog;
LogFrame* ConLogFrame;

wxCriticalSection g_cs_conlog;

static const uint max_item_count = 500;
static const uint buffer_size = 1024 * 64;

struct LogPacket
{
	wxString m_prefix;
	wxString m_text;
	wxString m_colour;

	LogPacket(const wxString& prefix, const wxString& text, const wxString& colour)
		: m_prefix(prefix)
		, m_text(text)
		, m_colour(colour)
	{
		
	}

	LogPacket()
	{
	}
};

struct _LogBuffer : public MTPacketBuffer<LogPacket>
{
	_LogBuffer() : MTPacketBuffer<LogPacket>(buffer_size)
	{
	}

	void _push(const LogPacket& data)
	{
		const u32 sprefix	= data.m_prefix.Len();
		const u32 stext		= data.m_text.Len();
		const u32 scolour	= data.m_colour.Len();

		m_buffer.Reserve(
			sizeof(u32) + sprefix +
			sizeof(u32) + stext +
			sizeof(u32) + scolour);

		u32 c_put = m_put;

		memcpy(&m_buffer[c_put], &sprefix, sizeof(u32));
		c_put += sizeof(u32);
		memcpy(&m_buffer[c_put], data.m_prefix.c_str(), sprefix);
		c_put += sprefix;

		memcpy(&m_buffer[c_put], &stext, sizeof(u32));
		c_put += sizeof(u32);
		memcpy(&m_buffer[c_put], data.m_text.c_str(), stext);
		c_put += stext;

		memcpy(&m_buffer[c_put], &scolour, sizeof(u32));
		c_put += sizeof(u32);
		memcpy(&m_buffer[c_put], data.m_colour.c_str(), scolour);
		c_put += scolour;

		m_put = c_put;
		CheckBusy();
	}

	LogPacket _pop()
	{
		LogPacket ret;

		u32 c_get = m_get;

		const u32& sprefix = *(u32*)&m_buffer[c_get];
		c_get += sizeof(u32);
		if(sprefix) memcpy(wxStringBuffer(ret.m_prefix, sprefix), &m_buffer[c_get], sprefix);
		c_get += sprefix;

		const u32& stext = *(u32*)&m_buffer[c_get];
		c_get += sizeof(u32);
		if(stext) memcpy(wxStringBuffer(ret.m_text, stext), &m_buffer[c_get], stext);
		c_get += stext;

		const u32& scolour = *(u32*)&m_buffer[c_get];
		c_get += sizeof(u32);
		if(scolour) memcpy(wxStringBuffer(ret.m_colour, scolour), &m_buffer[c_get], scolour);
		c_get += scolour;

		m_get = c_get;
		if(!HasNewPacket()) Flush();

		return ret;
	}
} LogBuffer;

LogWriter::LogWriter()
{
	if(!m_logfile.Open(_PRGNAME_ ".log", wxFile::write))
	{
		wxMessageBox("Cann't create log file! (" _PRGNAME_ ".log)", wxMessageBoxCaptionStr, wxICON_ERROR);
	}
}

void LogWriter::WriteToLog(wxString prefix, wxString value, wxString colour/*, wxColour bgcolour*/)
{
	if(ThreadBase* thr = GetCurrentNamedThread())
	{
		prefix = (prefix.IsEmpty() ? "" : prefix + " : ") + thr->GetThreadName();
	}

	if(m_logfile.IsOpened())
		m_logfile.Write((prefix.IsEmpty() ? wxEmptyString : "[" + prefix + "]: ") + value + "\n");

	if(!ConLogFrame) return;

	wxCriticalSectionLocker lock(g_cs_conlog);

	if(wxThread::IsMain())
	{
		while(LogBuffer.IsBusy()) wxYieldIfNeeded();
	}
	else
	{
		while(LogBuffer.IsBusy()) Sleep(1);
	}

	//if(LogBuffer.put == LogBuffer.get) LogBuffer.Flush();

	LogBuffer.Push(LogPacket(prefix, value, colour));
}

wxString FormatV(const wxString fmt, va_list args)
{
	int length = 256;
	wxString str;
	
	for(;;)
	{
		str.Clear();
		wxStringBuffer buf(str, length+1);
		memset(buf, 0, length+1);
		if(vsnprintf(buf, length, fmt, args) != -1) break;
		length *= 2;
	}

	return str;
}

void LogWriter::Write(const wxString fmt, ...)
{
	va_list list;
	va_start(list, fmt);

	const wxString& frmt = FormatV(fmt, list);

	va_end(list);

	WriteToLog("!", frmt, "White");
}

void LogWriter::Error(const wxString fmt, ...)
{
	va_list list;
	va_start(list, fmt);

	const wxString& frmt = FormatV(fmt, list);

	va_end(list);

	WriteToLog("E", frmt, "Red");
}

void LogWriter::Warning(const wxString fmt, ...)
{
	va_list list;
	va_start(list, fmt);

	const wxString& frmt = FormatV(fmt, list);

	va_end(list);

	WriteToLog("W", frmt, "Yellow");
}

void LogWriter::SkipLn()
{
	WriteToLog(wxEmptyString, wxEmptyString, "Black");
}

BEGIN_EVENT_TABLE(LogFrame, wxPanel)
	EVT_CLOSE(LogFrame::OnQuit)
END_EVENT_TABLE()

LogFrame::LogFrame(wxWindow* parent)
	: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(600, 500))
	, ThreadBase(false, "LogThread")
	, m_log(*new wxListView(this))
{
	m_log.InsertColumn(0, wxEmptyString);
	m_log.InsertColumn(1, "Log");
	m_log.SetBackgroundColour(wxColour("Black"));

	wxBoxSizer& s_main = *new wxBoxSizer(wxVERTICAL);
	s_main.Add(&m_log, 1, wxEXPAND);
	SetSizer(&s_main);
	Layout();

	Show();
	ThreadBase::Start();
}

LogFrame::~LogFrame()
{
}

bool LogFrame::Close(bool force)
{
	Stop();
	ConLogFrame = NULL;
	return wxWindowBase::Close(force);
}

void LogFrame::Task()
{
	while(!TestDestroy())
	{
		if(!LogBuffer.HasNewPacket())
		{
			Sleep(1);
			continue;
		}
		else
		{
			wxThread::Yield();
		}

		const LogPacket item = LogBuffer.Pop();

		while(m_log.GetItemCount() > max_item_count)
		{
			m_log.DeleteItem(0);
			wxThread::Yield();
		}

		const int cur_item = m_log.GetItemCount();

		m_log.InsertItem(cur_item, item.m_prefix);
		m_log.SetItem(cur_item, 1, item.m_text);
		m_log.SetItemTextColour(cur_item, item.m_colour);
		m_log.SetColumnWidth(0, -1);
		m_log.SetColumnWidth(1, -1);

		::SendMessage((HWND)m_log.GetHWND(), WM_VSCROLL, SB_BOTTOM, 0);
	}

	LogBuffer.Flush();
}

void LogFrame::OnQuit(wxCloseEvent& event)
{
	Stop();
	ConLogFrame = NULL;
	event.Skip();
}