#include "Engine.h"
#include "states/MainMenuState.h"
#include <SDL2/SDL_log.h>
#include <SDL2/SDL_mixer.h>
#include <pybind11/pybind11.h>
#include "scripting/PythonEngine.h"
#include "scripting/bindings/kariokaModule.h"
#include <iostream>

namespace py = pybind11;

Engine* Engine::s_instance = nullptr;

Engine::Engine()
{
    // Init systems
    InitSDL();
    InitImGui();
    InitAudio();
    InitPython();

    // TileMapManager
    tileMapManager.CreateMap("overworld", 32, 32);
    tileMapManager.CreateMap("dungeon", 16, 16);
    
    // Load assets ...
    
    // Run MainMenuState
    stateManager.PushState(std::make_unique<MainMenuState>(), *this);
}

Engine::~Engine()
{	
    ImGui_ImplSDLRenderer2_Shutdown();
    ImGui_ImplSDL2_Shutdown();
    ImGui::DestroyContext();

    Mix_CloseAudio();
    
    if (renderer) { SDL_DestroyRenderer(renderer); }
    if (window)   { SDL_DestroyWindow(window); }
    
    ShutdownPython();
    
    SDL_Quit();
}

void Engine::InitSDL()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Engine::InitSDL(): %s", SDL_GetError());
        exit(1);
    }

    window = SDL_CreateWindow("kariokaEngine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                              EngineConfig::DEFAULT_WINDOW_WIDTH, EngineConfig::DEFAULT_WINDOW_HEIGHT,
                              SDL_WINDOW_SHOWN);
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}

void Engine::InitImGui()
{
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    (void)io;
    ImGui::StyleColorsDark();
    ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
    ImGui_ImplSDLRenderer2_Init(renderer);
}

void Engine::InitAudio()
{
    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
    {
    }
    
    Mix_AllocateChannels(64);
    Mix_VolumeMusic(EngineConfig::DEFAULT_MUSIC_VOLUME);
    Mix_Volume(-1, EngineConfig::DEFAULT_SOUND_VOLUME);
}

void Engine::HandleEvents()
{
    SDL_Event e;
    while (SDL_PollEvent(&e))
    {
        ImGui_ImplSDL2_ProcessEvent(&e);
        if (e.type == SDL_QUIT)
        {
            isRunning = false;
        }
        
        if (e.type == SDL_KEYDOWN)
        {
            if (e.key.keysym.sym == SDLK_BACKQUOTE)
            {
                pythonConsole.Toggle();
                continue;
            }
        }
        
        stateManager.HandleEvents(*this, e);
    }
}

void Engine::Update(float dt)
{
    stateManager.Update(*this, dt);
}

void Engine::Render()
{
    SDL_RenderClear(renderer);
    
    stateManager.Render(*this);
    
    ImGui_ImplSDLRenderer2_NewFrame();
    ImGui_ImplSDL2_NewFrame();
    ImGui::NewFrame();
    stateManager.RenderImGui(*this);
    pythonConsole.Render();
    ImGui::Render();
    ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
    
    SDL_RenderPresent(renderer);
}

void Engine::Run()
{
    Uint64 lastTime = SDL_GetPerformanceCounter();
    while (isRunning)
    {
        Uint64 current = SDL_GetPerformanceCounter();
        float dt = (current - lastTime) / static_cast<float>(SDL_GetPerformanceFrequency());
        lastTime = current;
        
        HandleEvents();
        Update(dt);
        Render();
        
        // Hot-reload Python scripts maybe
        // ...
        
        // Safe State Transition
        if (pendingNewState)
        {
            stateManager.ChangeState(std::move(pendingNewState), *this);
            pendingNewState = nullptr;
        }
        else if (pendingPop)
        {
            stateManager.PopState(*this);
            pendingPop = false;
        }
    }
}

void Engine::InitPython()
{
    if (s_instance != nullptr)
    {
        throw std::runtime_error("Engine instance already created! Only one Engine allowed");
    }
    
    s_instance = this;
    
    PythonEngine::Get().Init();
    
    PythonEngine::Get().Exec(R"(
import karioka
karioka.log('Python module for kariokaEngine initialized')
e = karioka.engine()
e.DebugInfo()
)");
}

void Engine::ShutdownPython()
{
    PythonEngine::Get().Shutdown();
}

void Engine::RunPythonScript(const std::string& filename)
{
    try
    {
        py::eval_file(filename);
    }
    catch (const py::error_already_set& e)
    {
        SDL_Log("Python script error in %s:\n%s", filename.c_str(), e.what());
    }
}

void Engine::RequestStateChange(std::unique_ptr<BaseState> newState)
{
    pendingNewState = std::move(newState);
    pendingPop = false;
}

void Engine::RequestPopState()
{
    pendingPop = true;
    pendingNewState = nullptr;
}

Engine& Engine::Get()
{
    if (s_instance == nullptr)
    {
        throw std::runtime_error("Engine not yet constructed. Create Engine object in main() first");
    }
    return *s_instance;
}

void Engine::DebugInfo()
{
    SDL_Log("kariokaEngine v0.1 DebugInfo()");
}

py::list Engine::GetStateStack()
{
    return py::cast(stateManager.GetStack());
}

void Engine::PrintStates()
{
    auto stack = stateManager.GetStack();
    
    SDL_Log("=== Current State Stack ===");
    if (stack.empty())
    {
        SDL_Log("Stack is empty");
    }
    else
    {
        std::cout << "Stack size: " << stack.size() << "\n";
        
        for (size_t i = 0; i < stack.size(); ++i)
        {
            std::string prefix = (i == stack.size() - 1) ? "-> " : " ";
            std::cout << prefix << stack[i] << "\n";
        }
    }
    SDL_Log("===========================");
}

StateManager& Engine::GetStateManager()
{
    return stateManager;
}

const StateManager& Engine::GetStateManager() const
{
    return stateManager;
}