/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "stdafx.h"
#include "Logger/Logger.h"
#include "IOutStream.h"
#include "platform.h"

#include <boost/thread.hpp>
#include <boost/interprocess/detail/os_thread_functions.hpp> // for native thread id
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <cstdio>
#include "lock.h"


namespace NS_Logging
{

	Logger::Logger(const char* name): m_minimalCategory(e_debug), m_bufSize(4096),
		m_buffer(new boost::thread_specific_ptr<std::string>()), m_ostream(0), m_enabled(false), m_name(name)
	{
//		m_ostream = CreateStream(name);
	}
	//-------------------------------------------------------------------------------------------

	Logger::~Logger()
	{
		delete m_buffer;
		delete m_ostream;
	}
	//-------------------------------------------------------------------------------------------

	void Logger::SetMinimalCategory(Category cat)
	{
		m_minimalCategory = long(cat);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::SetMaxMessageSize(size_t size)
	{
		m_bufSize = (long)size;
	}
	//-------------------------------------------------------------------------------------------

	void Logger::Error(char const* format, ...)
	{
		va_list va;
		va_start(va, format);
		log(NS_Logging::e_error, NULL, format, va);
		va_end(va);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::Info(char const* format, ...)
	{
		va_list va;
		va_start(va, format);
		log(NS_Logging::e_info, NULL, format, va);
		va_end(va);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::Debug(char const* format, ...)
	{
		va_list va;
		va_start(va, format);
		log(NS_Logging::e_debug, NULL, format, va);
		va_end(va);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::Warning(char const* format, ...)
	{
		va_list va;
		va_start(va, format);
		log(NS_Logging::e_warning, NULL, format, va);
		va_end(va);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::Log(const char* format, ...)
	{
		va_list va;
		va_start(va, format);
		log(NS_Logging::e_info, NULL, format, va);
		va_end(va);
	}
	//-------------------------------------------------------------------------------------------

	void Logger::log(Category category, char const* functionName, char const* format, va_list va)
	{
		if (!m_enabled || category > m_minimalCategory)
			return;

		if (!m_buffer->get())
		{
			m_buffer->reset(new std::string(m_bufSize, '\0')); // allocate thread-local buffer
		}

		// format Log message
		std::string& buf = **m_buffer;
		buf.resize(m_bufSize);
		char* curBuf = &buf[0];
		size_t size = buf.size() - 1; // for trailing \n
		static char const* const categoryStr[] = {"[CRITICAL]", "[ERROR]", "[WARNING]", "[INFO]", "[DEBUG]", "[DUMP]"};
		int written;
		if (functionName)
		{
#if defined(PLATFORM_MAC)
			// boost::interprocess::detail::get_current_thread_id() returns _opaque_pthread_t
			// seems that _opaque_pthread_t is
			// struct _opaque_pthread_t { long __sig; struct __darwin_pthread_handler_rec *__cleanup_stack; char __opaque[1168]; };
			written = m_ostream->FormatMessage(curBuf, size, 0, categoryStr[category], functionName);
#else
 			written = m_ostream->FormatMessage(curBuf, (int)size, boost::interprocess::detail::get_current_thread_id(),
				categoryStr[category], functionName);
#endif
		} else
		{
#if defined(PLATFORM_MAC)
			written = m_ostream->FormatMessage(curBuf, size, 0, categoryStr[category]);
#else
			written = m_ostream->FormatMessage(curBuf, (int)size, boost::interprocess::detail::get_current_thread_id(),
				categoryStr[category]);
#endif
		}
		curBuf += written;
		size -= written;
		written = vsnprintf(curBuf, size, format, va);
		if (written != -1 && written < (int)size)
			written += (int)buf.size() - size - 1;
		else
			written = (int)buf.size() - 1;
		buf[written] = '\n';
		buf.resize(written + 1);

		if (m_ostream)
		{
 			static NS_DM_Client::NS_Common::CritSection critSection;
			NS_DM_Client::NS_Common::Lock lock(critSection);
			m_ostream->Write(&buf[0], (int)buf.size()); // thread safe operation
		}
		std::cout << buf;
	}
	//-------------------------------------------------------------------------------------------

	void Logger::enable()
	{
		m_enabled = true;
		m_ostream = CreateStream(m_name.c_str());
		std::cout << "Logger \"" << m_name << "\" is enabled\n";
	}
	//-------------------------------------------------------------------------------------------

	void FunctionNameLogHelper::Log(Logger& logger, char const* format, ...)
	{
		va_list va;
		va_start(va, format);
		logger.log(m_category, m_funcName, format, va);
		va_end(va);
		}
	//-------------------------------------------------------------------------------------------

	FunctionNameLogHelper::FunctionNameLogHelper(const char* funcName, Category cat):
		m_funcName(funcName), m_category(cat)
		{
		}

}
