#include "logger.h"

using namespace tools;

logger::logger()
	: m_curlv(LVWARN)
{}

logger::logger(const std::string filename, const std::string filetype)
	: m_curlv(LVWARN)
{
	open(filename, filetype);
}

logger::~logger()
{
	stop();
}

int
logger::open(const std::string filename, std::string filetype)
{
	m_filename = filename;
	m_filetype = filetype;
	SYSTEMTIME st = { 0 };
	GetLocalTime(&st);
	char file_path[MAX_PATH] = { 0 };
	m_last_day = st.wDay;
	sprintf(file_path, "%s%04u%02u%02u%s", filename.c_str(), st.wYear, st.wMonth, st.wDay, filetype.c_str());
	m_hfile = CreateFile(file_path,
		GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		NULL, OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL, NULL);

	if (INVALID_HANDLE_VALUE == m_hfile)
	{
		CloseHandle(m_hfile);
		m_run = false;
		return -1;
	}

	SetFilePointer(m_hfile, 0, NULL, FILE_END);

	m_run = true;
	if (!m_proc.joinable())
		m_proc = std::thread(&logger::processor, this);

	return 0;
}

void
logger::set_level(const log_level& level)
{
	if (level >= LVTRACK)
		m_curlv = level;

	if (level >= LVFATAL)
		m_curlv = LVFATAL;
}

void
logger::write(const char* msg, ...)
{
	if (NULL == msg)						return;
	if (false == m_run)						return;
	if (INVALID_HANDLE_VALUE == m_hfile)	return;

	raw_log tmp;
	tmp.level = LVWARN;
	GetLocalTime(&tmp.time);

	char buffer[2048];
	va_list arg_ptr;
	va_start(arg_ptr, msg);
	_vsnprintf(buffer, sizeof(buffer) - 1, msg, arg_ptr);
	va_end(arg_ptr);

	tmp.log.append(buffer);

	m_req_que.push(tmp);
}

void
logger::write(const log_level& level, const char* msg, ...)
{
	if (NULL == msg)						return;
	if (false == m_run)						return;
	if (INVALID_HANDLE_VALUE == m_hfile)	return;
	if (level < m_curlv)					return;

	raw_log tmp;
	tmp.level = level;
	GetLocalTime(&tmp.time);

	char buffer[2048];
	va_list arg_ptr;
	va_start(arg_ptr, msg);
	_vsnprintf(buffer, sizeof(buffer) - 1, msg, arg_ptr);
	va_end(arg_ptr);

	tmp.log.append(buffer);

	m_req_que.push(tmp);
}

#ifdef _DEBUG
#include <iostream>
#endif // _DEBUG

void
logger::processor()
{
	char buf[2048];
	std::shared_ptr<raw_log>	p_rlog;

	DWORD	write_length;

	char level[8];

	while (m_run)
	{
		while (m_run)
		{
			p_rlog = m_req_que.try_pop();
			if (std::shared_ptr<raw_log>() == p_rlog && m_run)
				Sleep(3);
			else
				break;
		}

		if (!m_run)	return;

		switch (p_rlog->level)
		{
		case LVTRACK:
			sprintf(level, "[TRACK]");
			break;
		case LVDEBUG:
			sprintf(level, "[DEBUG]");
			break;
		case LVWARN:
			sprintf(level, "[WARN]");
			break;
		case LVERROR:
			sprintf(level, "[ERROR]");
			break;
		case LVFATAL:
			sprintf(level, "[FATAL]");
			break;
		case LVNORMAL:
			sprintf(level, "");
			break;
		default:
			sprintf(level, "[WARN]");
			break;
		}
		if (m_last_day != p_rlog->time.wDay)
		{
			CloseHandle(m_hfile);
			char file_path[MAX_PATH] = { 0 };
			m_last_day = p_rlog->time.wDay;
			sprintf(file_path, "%s%04u%02u%02u%s", m_filename.c_str(), p_rlog->time.wYear, p_rlog->time.wMonth, p_rlog->time.wDay,m_filename.c_str());
			m_hfile = CreateFile(file_path,
				GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
				NULL, OPEN_ALWAYS,
				FILE_ATTRIBUTE_NORMAL, NULL);

			if (INVALID_HANDLE_VALUE == m_hfile)
			{
				CloseHandle(m_hfile);
				m_run = false;
				return;
			}

			SetFilePointer(m_hfile, 0, NULL, FILE_END);

			m_run = true;
		}
		if (m_filetype.compare(".csv")==0)
			sprintf(buf, "%d-%02d-%02d %02d:%02d:%2d.%03d,%s%s\r\n",
			p_rlog->time.wYear, p_rlog->time.wMonth, p_rlog->time.wDay,
			p_rlog->time.wHour, p_rlog->time.wMinute, p_rlog->time.wSecond,
			p_rlog->time.wMilliseconds, level, p_rlog->log.c_str());
		else
		sprintf(buf, "%d-%02d-%02d %02d:%02d:%2d.%03d: %s%s\r\n",
			p_rlog->time.wYear, p_rlog->time.wMonth, p_rlog->time.wDay,
			p_rlog->time.wHour, p_rlog->time.wMinute, p_rlog->time.wSecond,
			p_rlog->time.wMilliseconds, level, p_rlog->log.c_str());

		if (!WriteFile(m_hfile, buf, strlen(buf), &write_length, NULL))
		{
#ifdef _DEBUG
			std::cout << "write file error" << std::endl;
#endif // _DEBUG
		}
	}
}

void
logger::fsync()
{
	while (!m_req_que.empty());
}

void
logger::stop()
{
	fsync();
	m_run = false;
	if (m_proc.joinable())
		m_proc.join();
}