#include <cmath>
#include <engine/asset_cached/font_cache.h>
#include "engine.h"

#include "SDL3/SDL_init.h"
#include "SDL3/SDL_video.h"

#include "audio_manager.h"
#include "global_params.h"

bool Engine::init(int template_width, int template_height) {
    SENGINE &data = getInstance();
    data.template_width=static_cast<float>(template_width);
    data.template_height=static_cast<float>(template_height);

    const char* platformName = SDL_GetPlatform();
    std::string asset_path = GlobalParams::assetPath();

    //SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
    //SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");

    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)){
        SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError());
        throw SDL_GetError();
    }

    if (!TTF_Init()) {
        SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError());
        throw SDL_GetError();
    }


    if (!MIX_Init()) {
        SDL_Log("MIX_Init failed: %s", SDL_GetError());
        throw SDL_GetError();
    }

    if (!AudioManager::getInstance()->init()){
        SDL_Log("AudioManager init failed: %s", SDL_GetError());
        throw SDL_GetError();
    }

    // create a window
    if (strcmp(platformName, "Android") == 0) {
        data.window = SDL_CreateWindow("Пятнашки", template_width, template_height,  SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE);
    }else{
        data.window = SDL_CreateWindow("Пятнашки", template_width, template_height,  SDL_WINDOW_RESIZABLE);
    }
    if (!data.window){
        SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError());
        throw SDL_GetError();
    }

    // create a renderer
    data.renderer = SDL_CreateRenderer(data.window, NULL);
    if (!data.renderer){
        SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError());
        throw SDL_GetError();
    }

    SDL_Surface* icon = SDL_LoadSurface((asset_path+"launcher_logo.png").c_str());
    bool success = SDL_SetWindowIcon(data.window, icon);


    SDL_Log("SDL_GetWindowDisplayScale: %.f",SDL_GetWindowDisplayScale(data.window));

    return true;
}

void Engine::stop() {
    SDL_Log("=> Engine::stop()");
    SENGINE &data = getInstance();

    std::map<std::string, IScreen *> listIScreens = data.listIScreens;
    for (const auto& screen : listIScreens) {
        delete screen.second;
    }
    data.listIScreens.clear();

    AudioManager::getInstance()->stop();
    FontCache::quit();

    SDL_DestroyRenderer(data.renderer);
    SDL_DestroyWindow(data.window);

    MIX_Quit();
    TTF_Quit();
    SDL_Quit();
}

SDL_Window *Engine::getWindow() {
    SENGINE &data = getInstance();
    return data.window;
}

SDL_Renderer *Engine::getRenderer() {
    SENGINE &data = getInstance();
    return data.renderer;
}

// Получить соотношение экрана
float Engine::scale() {
    SENGINE &data = getInstance();
    int physicalWidth, physicalHeight;
    SDL_GetWindowSize(data.window, &physicalWidth, &physicalHeight);

    float scaleX =  static_cast<float>(physicalWidth) / data.template_width;
    float scaleY = static_cast<float>(physicalHeight) / data.template_height;

    return fmin(scaleX, scaleY);
}

// получить размер физического окна
SDL_FRect Engine::getWindowSize() {
    SENGINE &data = getInstance();
    int physicalWidth, physicalHeight;
    SDL_GetWindowSize(data.window, &physicalWidth, &physicalHeight);

    return SDL_FRect{0.0f,0.0f,static_cast<float>(physicalWidth), static_cast<float>(physicalHeight)};
}

// получить размер шаблоного окна
SDL_FRect Engine::getTemplateWindowSize() {
    SENGINE &data = getInstance();
    return SDL_FRect{0,0,data.template_width,data.template_height};

}

void Engine::deleteScreen(const std::string &name) {
    SENGINE &data = getInstance();

    if (data.listIScreens.contains(name)) {
        data.listIScreens.erase(name);
    }
}

bool Engine::setActiveScreen(const std::string &name, const VariantMap &args) {
    SENGINE &data = getInstance();
    if (data.listIScreens.contains(name)==0){
        return false;
    }
    data.activeScreen = data.listIScreens[name];
    data.activeScreen->onActive(args);
    return true;
}

void Engine::draw() {
    SENGINE &data = getInstance();
    if (!data.activeScreen) return;

    SDL_Color color = data.activeScreen->getBackgroundColor();

    SDL_SetRenderDrawColor(Engine::getRenderer(), color.r, color.g, color.b, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(Engine::getRenderer());

    data.activeScreen->drawAll();

    if (!SDL_RenderPresent(Engine::getRenderer())) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "RenderPresent failed: %s", SDL_GetError());
    }

}

void Engine::event(const SDL_Event &event) {
    SENGINE &data = getInstance();
    if (!data.activeScreen) return;

    data.activeScreen->eventAll(event);
}

void Engine::logic() {
    SENGINE &data = getInstance();
    if (!data.activeScreen) return;
    data.activeScreen->iterateAll();
}

void Engine::addIScreen(const std::string &name, IScreen *screen) {
    SENGINE &data = getInstance();
    data.listIScreens[name] = screen;
}

