/*
 * $Id: flexilog.cpp 4545 2017-05-30 19:11:23Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 *
 * Purpose      :
 *     Long description of what the file is for.
 */
#ifndef flexilog_h__
#define flexilog_h__

#include "flexilog.h"
#include "common.h"
#include "kernel.h"
#include "eventhandler.h"
//#include <wx/utils.h>
#include <wx/dir.h>
#include "VrutApi.h"

#ifndef __WXMSW__
	#ifndef _WIN32
		// this is for UNIX/Linux etc.
		#include <unistd.h>
		#include <sys/types.h>
		#include <sys/time.h>
		#include <sys/times.h>
		#include <sys/resource.h>
	#else
		#include <sys/timeb.h>
	#endif
#else //__WXMSW__
	// This is for MS Windows with MS Visual Studio compiler
	#include <windows.h>
	#include <wx/msw/winundef.h>
	#include <sys/types.h>
	#include <sys/timeb.h>
#endif

using namespace VRUT;

#include "wx/longlong.h"

//returns elapsed time in micro-seconds
wxULongLong VRUTGetTime(){

#ifndef __WXMSW__
#ifndef _WIN32
	// this is for UNIX/Linux etc
		struct timeval tv;
		//or use clock_gettime() ?
		gettimeofday(&tv,NULL);
		return tv.tv_sec*1000000 + tv.tv_usec;
#else
	// Windows without MS VS
		static struct _timeb mtime;
		//get local time
		_ftime_s(&mtime);

		return wxULongLong(1000000*mtime.time + mtime.millitm*1000);
#endif

#else // Windows with VS
	if (KERNEL->VRUTGetTimeHasHRTimer) {
		LARGE_INTEGER counter;
		//elapsed time since system was booted
		QueryPerformanceCounter(&counter);
		return wxULongLong(KERNEL->VRUTGetTimeFrequency*(double)counter.QuadPart);
	}
	else {
		static struct _timeb mtime;
		_ftime_s(&mtime);

		return wxULongLong(1000000*mtime.time + mtime.millitm*1000);
	}
#endif
}

#ifdef VRUT_HPC
FlexiLog::FlexiLog(const wxString &appPath) : dbgout((wxFileOutputStream *)NULL), dout((wxTextOutputStream *)NULL)
#else
FlexiLog::FlexiLog(wxTextCtrl * _textCtrl, const wxString &appPath) : wxLogTextCtrl(_textCtrl), textCtrl(_textCtrl), dbgout((wxFileOutputStream *)NULL), dout((wxTextOutputStream *)NULL)
#endif
{
	syncEveryMsg = false;
	SetLogLevel(4);
	SetVerbose(true);
#ifndef VRUT_HPC
	logToTextCtrl = true;
#endif
	wxString pathToLogFile;
#ifdef VRUT_HPC
#ifndef __WXMSW__ //Linux
	pathToLogFile=wxT("/var/log/");
	if (wxDir::Exists(pathToLogFile+wxT("VRUT")))
		pathToLogFile+=wxT("VRUT/");
#else
	//pathToLogFile=appPath;
	pathToLogFile = wxFileName::GetTempDir() + wxFileName::GetPathSeparator();
#endif
#else
	//pathToLogFile=appPath;
	pathToLogFile = wxFileName::GetTempDir()+wxFileName::GetPathSeparator();
#endif
	dbgout = new wxFileOutputStream(pathToLogFile+wxT("VRUT_")+wxGetHostName()+wxT(".log"));
	if (dbgout && dbgout->IsOk())
		dout = new wxTextOutputStream(*dbgout);
	/*//temporarily commented out, it crash if log file cannot be opened (perhaps because loging at this stage of VRUT startup is not working yet)
	else
		LogMessage(wxT("<FlexiLog>Logging to file disabled"), wxLOG_Warning);*/
}


FlexiLog::~FlexiLog()
{
	if (dbgout)
		dbgout->Close();
	SAFE_DELETE(dout);
	SAFE_DELETE(dbgout);
}


FlexiLog * FlexiLog::GetInstance()
{
	wxLog * logInst = wxLog::GetActiveTarget();
	if (logInst)
		return dynamic_cast<FlexiLog *>(logInst);

	return (FlexiLog *)NULL;
}


void FlexiLog::LogMessageFlexi(const wxString & msg, wxLogLevel loglevel)
{
#ifndef __WXMSW__
	if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
		wxMutexGuiEnter();
#endif
	FlexiLog * logInst = GetInstance();
	if (logInst && (GetLogLevel()>=loglevel))
		logInst->LogMessage(msg, loglevel);
#ifndef __WXMSW__
	if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
		wxMutexGuiLeave();
#endif
}


#if wxCHECK_VERSION(2, 9, 0)
void FlexiLog::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info)
{
	if ((KERNEL->GetMainThreadID()!=wxThread::GetCurrentId()) && (KERNEL->GetMessageSink()->eventHandlerThreadID!=wxThread::GetCurrentId()))
	{
		KERNEL->GetMessageSink()->PostEvent(GET_EVT_LOG(VRUTGetTime().ToString()+ wxT(" ") + msg + wxString::Format(wxT(" [async thread=%lu]"), (unsigned long)wxThread::GetCurrentId()), level));
		return;
	}
	wxString prep(wxT('\n') + wxDateTime::Now().FormatISOTime() + wxT(" ") + VRUTGetTime().ToString() + wxT(" "));
	switch (level)
	{
	case wxLOG_FatalError:
	case wxLOG_Error:
#ifndef VRUT_HPC
		if (logToTextCtrl)
			textCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
#endif
		prep+=wxT("Error: ");
		break;
	case wxLOG_Warning:
#ifndef VRUT_HPC
		if (logToTextCtrl)
			textCtrl->SetDefaultStyle(wxTextAttr(wxColour(128, 0, 0)));
#endif
		prep+=wxT("Warning: ");
		break;
	case wxLOG_Info:
	case wxLOG_Debug:
	case wxLOG_Trace:
	case wxLOG_Progress:
#ifndef VRUT_HPC
		if (logToTextCtrl)
			textCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
#endif
		prep+=wxT("Debug: ");
		break;
	case wxLOG_Message:
	case wxLOG_Status:
	default:
#ifndef VRUT_HPC
		if (logToTextCtrl)
			textCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
#endif
		break;
	}
	if (dout)
	{
		*dout << prep << msg;
		if (syncEveryMsg)
			dbgout->Sync();
	}
#ifndef VRUT_HPC
	if (logToTextCtrl)
	{
#ifndef __WXMSW__
		if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
			wxMutexGuiEnter();
#endif
		textCtrl->AppendText(prep + msg);
#ifndef __WXMSW__
		if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
			wxMutexGuiLeave();
#endif
	}
#endif //VRUT_HPC
}
#else
void FlexiLog::DoLog(wxLogLevel level, const wxChar * msg, time_t timestamp)
{
	if (dout)
	{
		wxString prep(wxT('\n'));
		TimeStamp(&prep);
		switch (level)
		{
		case wxLOG_FatalError:
		case wxLOG_Error:
			prep.Append(wxT("Error: "));
			break;
		case wxLOG_Warning:
			prep.Append(wxT("Warning: "));
			break;
		case wxLOG_Debug:
			prep.Append(wxT("Debug: "));
			break;
		case wxLOG_Info:
		case wxLOG_Message:
		case wxLOG_Status:
		case wxLOG_Trace:
		case wxLOG_Progress:
		default:
			break;
		}
		*dout << prep << msg;
		if (syncEveryMsg)
			dbgout->Sync();
	}

#ifndef VRUT_HPC
	if (logToTextCtrl)
	{
#ifndef __WXMSW__
		if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
			wxMutexGuiEnter();
#endif
		switch (level)
		{
		case wxLOG_FatalError:
		case wxLOG_Error:
			textCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
			break;
		case wxLOG_Warning:
			textCtrl->SetDefaultStyle(wxTextAttr(wxColour(128, 0, 0)));
			break;
		case wxLOG_Info:
		case wxLOG_Debug:
		case wxLOG_Trace:
		case wxLOG_Progress:
			textCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
			break;
		case wxLOG_Message:
		case wxLOG_Status:
		default:
			textCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
			break;
		}
		wxLogTextCtrl::DoLog(level, msg, timestamp);
#ifndef __WXMSW__
		if (KERNEL->GetMainThreadID() != wxThread::GetCurrentId())
			wxMutexGuiLeave();
#endif
	}
#endif //VRUT_HPC
}
#endif


void FlexiLog::LogMessage(const wxString & msg, wxLogLevel loglevel)
{
	if (KERNEL->GetMainThreadID() == wxThread::GetCurrentId()
#ifdef __WXMSW__
		|| KERNEL->GetMessageSink()->eventHandlerThreadID == wxThread::GetCurrentId()
#endif
	)
	{
		switch (loglevel)
		{
		case wxLOG_FatalError:
			wxLogFatalError(msg);
			break;
		case wxLOG_Error:
			wxLogError(msg);
			break;
		case wxLOG_Warning:
			wxLogWarning(msg);
			break;
		case wxLOG_Info:
			wxLogVerbose(msg);
			break;
		case wxLOG_Debug:
			wxLogDebug(msg);
			break;
		case wxLOG_Trace:
		case wxLOG_Progress:
		case wxLOG_Message:
			wxLogMessage(msg);
			break;
		case wxLOG_Status:
#ifdef VRUT_HPC
			wxLogVerbose(msg);//why not exists wxLogStatus without GUI?
#else
			wxLogStatus(msg);
#endif
			break;
		}
	}
	else
		KERNEL->GetMessageSink()->PostEvent(GET_EVT_LOG(VRUTGetTime().ToString()+ wxT(" ") + msg + wxString::Format(wxT(" [async thread=%lu]"), (unsigned long)wxThread::GetCurrentId()), loglevel));
}

#endif
