// 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 <Physics.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
{
    String g_gameName;
    String g_gamePackageName;
    String g_gameDeveloperName;
    String g_gamePublisherName;
    IA_VERSION_TYPE g_gameVersion{};

    SDL_Window *g_windowHandle;

    VOID __Internal_Engine::Initialize()
    {
        const auto config = Game_GetConfigRequest();

        IAE_LOG_INFO("Booting IAEngine for ", g_gameName);

        SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeRight");

        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_gameName.c_str(), 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_gameVersion;
        SDL_SetAppMetadata(g_gameName.c_str(), IA_STRINGIFY_VERSION(gameVersion).c_str(),
                           g_gamePackageName.c_str());

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

        Game_OnInitialize();
    }

    VOID __Internal_Engine::Terminate()
    {
        Game_OnTerminate();

        Physics::Terminate();
        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();
        Physics::Update();
        WorldManager::Update();
        WorldManager::FixedUpdate();
        Game_OnUpdate(Time::GetFrameDeltaTime());
        Game_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);
    }

    INT32 Run(IN CONST String& name, IN CONST String& packageName, IN CONST String& developerName, IN CONST String& publisherName, IN IA_VERSION_TYPE version)
    {
        g_gameName = name;
        g_gameVersion = version;
        g_gamePackageName = packageName;
        g_gameDeveloperName = developerName;
        g_gamePublisherName = publisherName;

        __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
