#include <window/window.hpp>

namespace Game::Window {
    std::mutex Window::sMutex;

    Window::Window() : mWindow(nullptr), mRenderer(), mGameManager(), mRunning(false) { }

    Window::~Window() {
        mRenderer.destroy();

        // Try to kill the game slave thread
        if (mGameThread.joinable()) {
            mGameThread.request_stop();
            mGameThread.join();
            LOG("Game thread stopped successfully");
        }

        if (mWindow) {
            SDL_DestroyWindow(mWindow);
            mWindow = nullptr;
            sWindowBackend = nullptr;
            LOG("Window destroyed successfully");
        }
        SDL_Quit();
    }

    bool Window::init(int width, int height, const std::string& title) {
        if (!SDL_Init(SDL_INIT_VIDEO)) {
            ERROR("Failed to initialize SDL: " << SDL_GetError());
            return false;
        }

        if (!TTF_Init()) {
            ERROR("Failed to initialize SDL_ttf: " << SDL_GetError());
            SDL_Quit();
            return false;
        }

        Audio::Audio::getInstance().init();

        mWindow = SDL_CreateWindow(title.c_str(), width, height, SDL_WINDOW_RESIZABLE);
        if (!mWindow) {
            ERROR("Failed to create window: " << SDL_GetError());
            SDL_Quit();
            return false;
        }
        sWindowBackend = mWindow;

        LOG("Window created successfully");

        if (!mRenderer.init(mWindow)) {
            SDL_DestroyWindow(mWindow);
            mWindow = nullptr;
            sWindowBackend = nullptr;
            SDL_Quit();
            return false;
        }

        mGameThread = std::jthread(std::bind_front(&Game::GameManager::run, &mGameManager));

        mRunning = true;

        return true;
    }

    void Window::run() {
        SDL_Event event;
        while (mRunning) {
            SDL_PumpEvents();
            while (SDL_PollEvent(&event)) {
                if (event.type == SDL_EVENT_QUIT) {
                    mRunning = false;
                }

                // Window resize event - update the renderer's viewport
                if (event.type == SDL_EVENT_WINDOW_RESIZED) {
                    std::scoped_lock lock(mMutex);
                    SDL_SetRenderViewport(mRenderer.getSDLRenderer(), nullptr);

                    // Notify entities of the window resize so they can adjust if necessary
                    int newWidth, newHeight;
                    SDL_GetWindowSizeInPixels(mWindow, &newWidth, &newHeight);
                    auto entities = State::GameState::getInstance().getEntitiesSnapshot();
                    for (auto* entity : entities) {
                        if (entity) {
                            entity->onWindowResized(newWidth, newHeight);
                        }
                    }
                }
            }

            mRenderer.renderFrame();
            SDL_Delay(1000 / mTargetFPS); // Delay to cap the frame rate to the target FPS

            // Set the window title to show the current FPS for testing
            mFrameCount++;
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - mLastFPSTime).count();
            if (elapsed >= 1) {
                int fps = static_cast<int>(mFrameCount / elapsed);
                SDL_SetWindowTitle(mWindow, ("Game Window - FPS: " + std::to_string(fps)).c_str());
                mFrameCount = 0;
                mLastFPSTime = now;
            }
        }
    }
}