/******************************************************************************
*	File		-	render_context.cpp
*	Author		-	Joey Pollack
*	Date		-	2022/07/13 (y/m/d)
*	Mod Date	-	2022/07/13 (y/m/d)
*	Description	-	General setup/shutdown for the render system
******************************************************************************/

#include "render_context.h"
#include <platform/window.h>
#include "renderer2D.h"
#include <utils/logger.h>

namespace lunarium
{
    RenderContext::RenderContext()
        : mIsInit(false), mpWindow(nullptr)
    {

    }

    OpRes RenderContext::Initialize(Window* pWindow, Renderer2D** ppR2D, bool enableDebugMessages)
    { 
		if (mIsInit)
        {
            return OpRes::Fail("Can not initialize Render Context. It is already initialized.");
        }

        if (!pWindow->IsInit())
        {
            return OpRes::Fail("Can not initialize Render Context. The Window must be initialized first!");
        }

       if ((*ppR2D))
        {
            return OpRes::Fail("Can not initialize Render Context. The Renderer2D pointer should be null.");
        }


        mpWindow = pWindow;

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        if (enableDebugMessages)
		{
			InitDebugMsgSystem();
		}

        // Init render subsystems
		(*ppR2D) = new Renderer2D;
		OpRes result = (*ppR2D)->Initialize();
		if (Failed(result))
		{
			return OpRes::Fail("Failed to initialize the renderer2D subsystem: %s", result.Description.c_str());
		}

		// TODO: Init text rendering
		// OpRes res = mText.Initialize();
		// if (Failed(res))
		// {
		// 	Logger::Warn(LogCategory::GRAPHICS, "Could not initialized the text renderer - %s", res.Description);
		// }

		// Load the default internal font
		// const char* font = "OpenSans-Regular.ttf";
		// // GenerateFontFileAt(font);
		// mDefaultFont = mText.LoadFont(OpenFontData, OpenDataSize, font);
		// if (mDefaultFont < 0)
		// {
		// 	Logger::Warn(LogCategory::GRAPHICS, "Unable to load the default font: %s", font);
		// }
		// else
		// {
		// 	Logger::Info(LogCategory::GRAPHICS, "Successfully created default font: %s", font);
		// }

        return OpRes::OK();
    }

    void RenderContext::Shutdown(Renderer2D** ppR2D)
    {
        (*ppR2D)->Shutdown();
		(*ppR2D) = nullptr;
    }

    void RenderContext::SwapBuffers()
    {
        mpWindow->SwapBuffers();
    }

    ////////////////////////////////////////////////////////////
    //				DEBUG MESSAGE CALLBACK
    ////////////////////////////////////////////////////////////
    std::map<int, std::string> RenderContext::mDebugMsgTypes;
	std::map<int, std::string> RenderContext::mDebugMsgSources;
	std::map<int, std::string> RenderContext::mDebugMsgSeverity;

	void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id,
		GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
	{
		
		if (type == GL_DEBUG_TYPE_ERROR)
		{
			Logger::Error(LogCategory::GRAPHICS,
			"%s (type: %s, source: %s, severity: %s), message: %s",
			(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
			RenderContext::mDebugMsgSources[source].c_str(),
			RenderContext::mDebugMsgTypes[type].c_str(),
			RenderContext::mDebugMsgSeverity[severity].c_str(),
			message);
		}
		else
		{
			Logger::GIDebug(LogCategory::GRAPHICS,
			"%s (type: %s, source: %s, severity: %s), message: %s",
			(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
			RenderContext::mDebugMsgSources[source].c_str(),
			RenderContext::mDebugMsgTypes[type].c_str(),
			RenderContext::mDebugMsgSeverity[severity].c_str(),
			message);
		}
	}

    void RenderContext::InitDebugMsgSystem()
    {
        // DEBUG MESSAGE TYPES
		mDebugMsgTypes[GL_DEBUG_TYPE_ERROR] = "ERROR";
		mDebugMsgTypes[GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR] = "DEPRECATED_BEHAVIOR";
		mDebugMsgTypes[GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR] = "UNDEFINED_BEHAVIOR";
		mDebugMsgTypes[GL_DEBUG_TYPE_PORTABILITY] = "PORTABILITY";
		mDebugMsgTypes[GL_DEBUG_TYPE_PERFORMANCE] = "PERFORMANCE";
		mDebugMsgTypes[GL_DEBUG_TYPE_OTHER] = "OTHER";
		mDebugMsgTypes[GL_DEBUG_TYPE_MARKER] = "MARKER";
		mDebugMsgTypes[GL_DEBUG_TYPE_PUSH_GROUP] = "PUSH_GROUP";
		mDebugMsgTypes[GL_DEBUG_TYPE_POP_GROUP] = "POP_GROUP";

		// DEBUG MESSAGE SOURCES
		mDebugMsgSources[GL_DEBUG_SOURCE_API] = "API";
		mDebugMsgSources[GL_DEBUG_SOURCE_WINDOW_SYSTEM] = "WINDOW_SYSTEM";
		mDebugMsgSources[GL_DEBUG_SOURCE_SHADER_COMPILER] = "SHADER_COMPILER";
		mDebugMsgSources[GL_DEBUG_SOURCE_THIRD_PARTY] = "THIRD_PARTY";
		mDebugMsgSources[GL_DEBUG_SOURCE_APPLICATION] = "APPLICATION";
		mDebugMsgSources[GL_DEBUG_SOURCE_OTHER] = "OTHER";

		// DEBUG MESSAGE SEVERITY
		mDebugMsgSeverity[GL_DEBUG_SEVERITY_HIGH] =	"HIGH";
		mDebugMsgSeverity[GL_DEBUG_SEVERITY_MEDIUM] = "MEDIUM";
		mDebugMsgSeverity[GL_DEBUG_SEVERITY_LOW] = "LOW";
		mDebugMsgSeverity[GL_DEBUG_SEVERITY_NOTIFICATION] = "NOTIFICATION";

		glEnable(GL_DEBUG_OUTPUT);
		glDebugMessageCallback(MessageCallback, 0);
    }

}