/*
 * 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 "LoggerFactory.h"
#include <fstream>
#include <iostream>
#include "Logger/Logger.h"
#include "lock.h"

namespace NS_Logging
{
#ifdef PLATFORM_WINDOWS
	static const char* S_configFileName = "%ALLUSERSPROFILE%/Funambol/DMClient/Logs/LogRequired.txt";
#else
	static const char* S_configFileName = "/etc/Funambol/DMClient/Logs/LogRequired.txt";
#endif

	static const std::string S_allLoggers = "*";

	void LoggerFactory::destroy()
	{
		delete ms_instance;
		ms_instance = 0;
	}
	//-------------------------------------------------------------------------------------------

	LoggerFactory* LoggerFactory::ms_instance = 0;
	NS_DM_Client::NS_Common::CritSection LoggerFactory::ms_critSection;
	int LoggerFactory::ms_defaultLevel = 0;

	LoggerFactory::LoggerFactory()
	{
		readConfiguration();
	}
	//-------------------------------------------------------------------------------------------

	LoggerFactory& LoggerFactory::GetInstance()
	{
		if (!ms_instance)
		{
			NS_DM_Client::NS_Common::Lock lock(ms_critSection);
			if (!ms_instance)
			{
				ms_instance = new LoggerFactory();
				if (::atexit(destroy))
				{
					destroy();
				}
			}
		}
		return *ms_instance;
	}
	//-------------------------------------------------------------------------------------------

	LoggerFactory::~LoggerFactory()
	{
		Release();
	}
	//-------------------------------------------------------------------------------------------

	void LoggerFactory::Release()
	{
		Loggers::iterator end(m_loggers.end());
		for (Loggers::iterator iter = m_loggers.begin(); iter != end; ++iter)
		{
			Logger* logger = iter->second;
			delete logger;
		}
		m_loggers.clear();
	}
	//-------------------------------------------------------------------------------------------

	void LoggerFactory::readConfiguration()
	{
		std::ifstream configFile;

		std::string configFileName(S_configFileName);
#ifdef PLATFORM_WINDOWS
		const size_t BUFFER_SIZE  = 32767;
		std::vector<char>  buf;
		buf.resize(BUFFER_SIZE);
		// MSDN: When using ANSI strings, the buffer size should be the string length, plus terminating null character, plus one.
		::ExpandEnvironmentStringsA(S_configFileName, &buf[0], buf.size() - 2);
		configFileName = &buf[0];
#endif
		// if file doesn't exist - logging process is disabled
		configFile.open(configFileName.c_str());
		std::cout << "Configuration file is = " << configFileName.c_str() << std::endl;

		//getdefaultLevel(configFile); // first line - default level for all instances
		std::string loggerInfo;
		bool isDefaultLevel = false;
		while (getline(configFile, loggerInfo))
		{
			if (loggerInfo.length() && '\r' == loggerInfo[loggerInfo.length()-1])
				loggerInfo[loggerInfo.length()-1] = '\0';

			LoggerName loggerName;
			size_t level;
			expandLoggerInfo(loggerInfo, loggerName, level);
			if (loggerName != S_allLoggers)
			{
				Loggers::iterator found = m_loggers.find(loggerName);
				if (m_loggers.end() == found)
				{
					Logger* logger = new Logger(loggerName.c_str());
					logger->enable();
					logger->SetMinimalCategory((NS_Logging::Category)level);
					m_loggers[loggerName] = logger;
				}
			}
			else
			{
				isDefaultLevel = true;
				ms_defaultLevel = level;
			}
		}
		if (isDefaultLevel)
		{
			setDefaultCategoryForAllLogers();
		}
		configFile.close();
	}
	//-------------------------------------------------------------------------------------------

	void LoggerFactory::setDefaultCategoryForAllLogers()
	{
		Loggers::iterator end = m_loggers.end();
		for (Loggers::iterator iter = m_loggers.begin(); iter != end; ++iter)
		{
			iter->second->SetMinimalCategory((NS_Logging::Category)ms_defaultLevel);
		}

	}
	//-------------------------------------------------------------------------------------------

	void LoggerFactory::getdefaultLevel(std::ifstream& configFile)
	{
		std::string defaultLevel;
		getline(configFile, defaultLevel);
		ms_defaultLevel = atoi(defaultLevel.c_str());
	}
	//-------------------------------------------------------------------------------------------

	void LoggerFactory::expandLoggerInfo(const std::string& loggerInfo, LoggerName& loggerName, size_t& level)
	{ // loggerName level
		const std::string seperator(" ");
		std::string::size_type endName = loggerInfo.find_first_of(seperator);
		if (endName == std::string::npos)
		{
			loggerName = loggerInfo;
			level = ms_defaultLevel;
		}
		else
		{
			loggerName = loggerInfo.substr(0, endName);
			size_t beginLevel = endName + seperator.length();
			std::string::size_type endLevel = loggerInfo.find_first_of(seperator, beginLevel);
			std::string strLevel;
			if (endName == std::string::npos)
			{
				strLevel = loggerInfo.substr(beginLevel, endLevel);
			}
			else
			{
				strLevel = loggerInfo.substr(beginLevel, endLevel - beginLevel);
			}
			level = atoi(strLevel.c_str());
		}
	}
	//-------------------------------------------------------------------------------------------

	Logger& LoggerFactory::GetLogger(const char* name)
	{
		Loggers::iterator found = m_loggers.find(name);
		Logger* logger = NULL;
		if (m_loggers.end() == found)
		{
			logger = new Logger(name);
			m_loggers[name] = logger;

			return *logger;
		}
		return *found->second;
	}
	//-------------------------------------------------------------------------------------------

	Logger& GetLogger(const char* name)
	{
		return NS_Logging::LoggerFactory::GetInstance().GetLogger(name);
	}
	//-------------------------------------------------------------------------------------------

	void ReleaseLoggers()
	{
		NS_Logging::LoggerFactory::GetInstance().Release();
	}
}
