/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmArray.h"
#include "glmMap.h"
#include "glmString.h"
#include <sstream>

#include "glmMutex.h"
#include "glmScopedLock.h"

namespace glm
{
	class ILogger;
	class Log;

	// Log class publishing trace messages to registered loggers.
	//
	// For each level of severity, a macro is defined, which adds automatically
	// the following information about the origin of the trace message :
	// - name of file from which the message was sent
	// - line from which the message was sent
	// - name of the operation from which the message was sent
	// The content of the trace message can be specified with a c++ stream format
	class GLM_CORE_API Log
	{
	public:

		// possible severity levels for a log message.
        enum Severity
        {
            LOG_ERROR,   // errors, continuing execution is hazardous
            LOG_WARNING, // something is wrong with input data (host, DB, configuration) and should be fixed, but execution can continue, possibly not as expected
            LOG_INFO,    // informative messages
            LOG_DEBUG,   // debug messages
        };

        enum Module
		{
			SDK,
			CROWD, 
			DCC
		};

		struct GLM_CORE_API TraceKey
		{
			int32_t _counter;
			glm::Log* _log;
			TraceKey(int32_t counter = 0);
			~TraceKey();
		};

		struct GLM_CORE_API TraceCounter
		{
			GlmString _trace;
		};

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared);

		Log();
		~Log();

		bool mustLog(Module module, Severity severity);
		bool mustLog(Module module, Severity severity, TraceKey& TraceKey);
		void trace(Module module, Severity severity, const char* msg, const char* file, int line, const char* operation);
		void trace(Module module, Severity severity, TraceKey& TraceKey, const char* msg, const char* file, int line, const char* operation);
		void addLogger(ILogger* logger);
		void removeLogger(ILogger* logger);

		void traceCounts(Module module);
		void resetTraceCounts();

		Severity _logSeverity[3]; // SDK, CROWD, DCC
		int _maxTraceCount;
		GlmMap<TraceKey*, TraceCounter> _traceCounts[4];

	private:

		GLM_DISALLOW_COPY_AND_ASSIGN(Log);
		Array<ILogger*> _loggers;
	};

	class GLM_CORE_API ILogger
	{
	public:
		GLM_ABSTRACTCLASS_STAT_ID(glm::MemoryStatIds::GolaemShared)

		ILogger();
		virtual ~ILogger();
		virtual void trace(Log::Module module, Log::Severity severity, const char* msg, const char* file, int line, const char* operation) = 0;
	};

	// Golaem Log
	GLM_CORE_API Log* getLog();
	void allocateLog();
	void deallocateLog();

	typedef glm::SpinLock LogMutexType;
	extern GLM_CORE_API LogMutexType gLogMutex;

	//-------------------------------------------------------------------------
	inline void Log::addLogger(ILogger* logger)
	{
		ScopedLock<LogMutexType> lock(gLogMutex);
		_loggers.push_back(logger);
	}

	//-------------------------------------------------------------------------
	#define GLM_TRACE_LOG_LIMIT(MODULE, SEVERITY, MSG) { \
		static glm::Log::TraceKey TraceKey(0); \
		if (glm::getLog()->mustLog(MODULE, SEVERITY, TraceKey)) \
		{ \
			std::stringstream logConcat; \
			logConcat << MSG; \
			glm::getLog()->trace(MODULE, SEVERITY, TraceKey, logConcat.str().c_str(), __FILE__, __LINE__, __FUNCTION__); \
		} \
	}

	//-------------------------------------------------------------------------
	#define GLM_TRACE_LOG_NOLIMIT(MODULE, SEVERITY, MSG) { \
		if (glm::getLog()->mustLog(MODULE, SEVERITY)) \
		{ \
			std::stringstream logConcat; \
			logConcat << MSG; \
			glm::getLog()->trace(MODULE, SEVERITY, logConcat.str().c_str(), __FILE__, __LINE__, __FUNCTION__); \
		} \
	}

#define GLM_SDK_TRACE_LOG(SEVERITY, MSG) GLM_TRACE_LOG_NOLIMIT(glm::Log::SDK, SEVERITY, MSG)
#define GLM_SDK_TRACE_ERROR(MSG) GLM_SDK_TRACE_LOG(glm::Log::LOG_ERROR, MSG)
#define GLM_SDK_TRACE_WARNING(MSG) GLM_SDK_TRACE_LOG(glm::Log::LOG_WARNING, MSG)
#define GLM_SDK_TRACE_INFO(MSG) GLM_SDK_TRACE_LOG(glm::Log::LOG_INFO, MSG)
#define GLM_SDK_TRACE_DEBUG(MSG) GLM_SDK_TRACE_LOG(glm::Log::LOG_DEBUG, MSG)

#define GLM_CROWD_TRACE_LOG(SEVERITY, MSG) GLM_TRACE_LOG_NOLIMIT(glm::Log::CROWD, SEVERITY, MSG)
#define GLM_CROWD_TRACE_ERROR(MSG) GLM_CROWD_TRACE_LOG(glm::Log::LOG_ERROR, MSG)
#define GLM_CROWD_TRACE_WARNING(MSG) GLM_CROWD_TRACE_LOG(glm::Log::LOG_WARNING, MSG)
#define GLM_CROWD_TRACE_INFO(MSG) GLM_CROWD_TRACE_LOG(glm::Log::LOG_INFO, MSG)
#define GLM_CROWD_TRACE_DEBUG(MSG) GLM_CROWD_TRACE_LOG(glm::Log::LOG_DEBUG, MSG)

#define GLM_DCC_TRACE_LOG(SEVERITY, MSG) GLM_TRACE_LOG_NOLIMIT(glm::Log::DCC, SEVERITY, MSG)
#define GLM_DCC_TRACE_ERROR(MSG) GLM_DCC_TRACE_LOG(glm::Log::LOG_ERROR, MSG)
#define GLM_DCC_TRACE_WARNING(MSG) GLM_DCC_TRACE_LOG(glm::Log::LOG_WARNING, MSG)
#define GLM_DCC_TRACE_INFO(MSG) GLM_DCC_TRACE_LOG(glm::Log::LOG_INFO, MSG)
#define GLM_DCC_TRACE_DEBUG(MSG) GLM_DCC_TRACE_LOG(glm::Log::LOG_DEBUG, MSG)

#define GLM_SDK_TRACE_LOG_LIMIT(SEVERITY, MSG) GLM_TRACE_LOG_LIMIT(glm::Log::SDK, SEVERITY, MSG)
#define GLM_SDK_TRACE_ERROR_LIMIT(MSG) GLM_SDK_TRACE_LOG_LIMIT(glm::Log::LOG_ERROR, MSG)
#define GLM_SDK_TRACE_WARNING_LIMIT(MSG) GLM_SDK_TRACE_LOG_LIMIT(glm::Log::LOG_WARNING, MSG)
#define GLM_SDK_TRACE_INFO_LIMIT(MSG) GLM_SDK_TRACE_LOG_LIMIT(glm::Log::LOG_INFO, MSG)
#define GLM_SDK_TRACE_DEBUG_LIMIT(MSG) GLM_SDK_TRACE_LOG_LIMIT(glm::Log::LOG_DEBUG, MSG)

#define GLM_CROWD_TRACE_LOG_LIMIT(SEVERITY, MSG) GLM_TRACE_LOG_LIMIT(glm::Log::CROWD, SEVERITY, MSG)
#define GLM_CROWD_TRACE_ERROR_LIMIT(MSG) GLM_CROWD_TRACE_LOG_LIMIT(glm::Log::LOG_ERROR, MSG)
#define GLM_CROWD_TRACE_WARNING_LIMIT(MSG) GLM_CROWD_TRACE_LOG_LIMIT(glm::Log::LOG_WARNING, MSG)
#define GLM_CROWD_TRACE_INFO_LIMIT(MSG) GLM_CROWD_TRACE_LOG_LIMIT(glm::Log::LOG_INFO, MSG)
#define GLM_CROWD_TRACE_DEBUG_LIMIT(MSG) GLM_CROWD_TRACE_LOG_LIMIT(glm::Log::LOG_DEBUG, MSG)
}
#define GLM_DCC_TRACE_LOG_LIMIT(SEVERITY, MSG) GLM_TRACE_LOG_LIMIT(glm::Log::DCC, SEVERITY, MSG)
#define GLM_DCC_TRACE_ERROR_LIMIT(MSG) GLM_DCC_TRACE_LOG_LIMIT(glm::Log::LOG_ERROR, MSG)
#define GLM_DCC_TRACE_WARNING_LIMIT(MSG) GLM_DCC_TRACE_LOG_LIMIT(glm::Log::LOG_WARNING, MSG)
#define GLM_DCC_TRACE_INFO_LIMIT(MSG) GLM_DCC_TRACE_LOG_LIMIT(glm::Log::LOG_INFO, MSG)
#define GLM_DCC_TRACE_DEBUG_LIMIT(MSG) GLM_DCC_TRACE_LOG_LIMIT(glm::Log::LOG_DEBUG, MSG)
