// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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 General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <AudioManager.hpp>
#include <EventManager.hpp>
#include <InputManager.hpp>
#include <InternalEngine.hpp>
#include <Random.hpp>
#include <Renderer/Renderer.hpp>
#include <ResourceManager.hpp>
#include <Time.hpp>
#include <WorldManager.hpp>

#include <IAEngine/Engine.hpp>
#include <IAEngine/EngineLibraryInterface.hpp>

#include <IAEngine/imgui/backends/imgui_impl_sdl3.h>
#include <IAEngine/imgui/backends/imgui_impl_sdlgpu3.h>

namespace ia::iae
{
    GameFunctionTable g_gameFunctions{};

    SDL_Window *g_windowHandle;

    VOID __Internal_Engine::Initialize()
    {
        const auto config = g_gameFunctions.GetConfigRequest();

        IAE_LOG_INFO("Booting IAEngine for ", g_gameFunctions.GetName());

        if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD))
            THROW_UNKNOWN("Failed to intialize SDL: ", SDL_GetError());

        if (!(g_windowHandle = SDL_CreateWindow(g_gameFunctions.GetName(), config.ScreenWidth, config.ScreenHeight,
                                                SDL_WINDOW_RESIZABLE)))
            THROW_UNKNOWN("Failed to create the SDL window: ", SDL_GetError());

        // SDL_SetWindowResizable(g_windowHandle, false);

        const auto gameVersion = g_gameFunctions.GetVersion();
        SDL_SetAppMetadata(g_gameFunctions.GetName(), BuildString(gameVersion).c_str(),
                           g_gameFunctions.GetPackageName());

        Time::Initialize();
        Random::Initialize();
        Renderer::Initialize();
        AudioManager::Initialize();
        EventManager::Initialize();
        InputManager::Initialize();
        ResourceManager::Initialize();
        WorldManager::Initialize();
        UI::Initialize();

        g_gameFunctions.OnInitialize();
    }

    VOID __Internal_Engine::Terminate()
    {
        g_gameFunctions.OnTerminate();

        UI::Terminate();
        WorldManager::Terminate();
        ResourceManager::Terminate();
        InputManager::Terminate();
        EventManager::Terminate();
        AudioManager::Terminate();
        Renderer::Terminate();
        Random::Terminate();
        Time::Terminate();

        SDL_DestroyWindow(g_windowHandle);

        IAE_LOG_INFO("Shutting down IAEngine");
    }

    VOID __Internal_Engine::Iterate()
    {
        UI::Update();
        WorldManager::Update();
        WorldManager::FixedUpdate();
        g_gameFunctions.OnUpdate(Time::GetFrameDeltaTime());
        g_gameFunctions.OnFixedUpdate();

        Renderer::BeginFrame();
        WorldManager::Draw();
        UI::Draw();
        Renderer::EndFrame();
    }

    VOID __Internal_Engine::ProcessEvent(IN SDL_Event *event)
    {
        switch (event->type)
        {
        case SDL_EVENT_WINDOW_RESIZED:
            Engine::ResizeDisplay(event->window.data1, event->window.data2);
            break;
        }
        InputManager::OnSDLEvent(event);
        EventManager::OnSDLEvent(event);
        UI::OnSDLEvent(event);
        ImGui_ImplSDL3_ProcessEvent(event);
    }

    FLOAT32 TimePeriod::GetValue() CONST
    {
        return (FLOAT32) m_value + ((FLOAT32) m_value * (Random::Get() * m_randomAdjustment) / 100.0f);
    }

    BOOL ValidateGameFunctionTable(IN GameFunctionTable gameFunctionTable)
    {
        if (!gameFunctionTable.GetConfigRequest)
            return false;
        if (!gameFunctionTable.OnInitialize)
            return false;
        if (!gameFunctionTable.OnTerminate)
            return false;
        if (!gameFunctionTable.OnDebugDraw)
            return false;
        if (!gameFunctionTable.OnFixedUpdate)
            return false;
        if (!gameFunctionTable.OnUpdate)
            return false;
        if (!gameFunctionTable.OnResize)
            return false;
        if (!gameFunctionTable.GetName)
            return false;
        if (!gameFunctionTable.GetVersion)
            return false;
        if (!gameFunctionTable.GetPackageName)
            return false;
        if (!gameFunctionTable.GetDeveloperName)
            return false;
        if (!gameFunctionTable.GetPublisherName)
            return false;
        return true;
    }

    INT32 Run(IN GameFunctionTable gameFunctionTable)
    {
        if (!ValidateGameFunctionTable(gameFunctionTable))
        {
            IAE_LOG_ERROR("Invalid game function table was passed to the engine. Exiting..");
            return -1;
        }
        g_gameFunctions = gameFunctionTable;

        __Internal_Engine::Initialize();

        SDL_Event event{};
        while (true)
        {
            SDL_PollEvent(&event);
            if (event.type == SDL_EVENT_QUIT)
                break;
            __Internal_Engine::ProcessEvent(&event);
            __Internal_Engine::Iterate();
        }

        __Internal_Engine::Terminate();

        return 0;
    }
} // namespace ia::iae
