#pragma once

#include <SDL3/SDL.h>

#include <cstdint>
#include <memory>

#include "core/rendering/shader_backend.hpp"

class Screen {
   public:
    static void init();
    static void destroy();
    static auto get() -> Screen*;

    ~Screen();  // públic per a std::unique_ptr

    // Presentació — rep el buffer ARGB de 320x200 de JD8
    void present(Uint32* pixel_data);

    // Gestió de finestra
    void toggleFullscreen();
    void incZoom();
    void decZoom();
    void setZoom(int zoom);

    // Shaders i vídeo
    // Mètodes que depenen d'una precondició (GPU present, shaders on, etc.)
    // retornen `bool`: true si l'acció s'ha aplicat, false si la precondició
    // no es complia. Els callers (F-keys, menú) poden suprimir notificacions
    // o feedback quan la crida no ha tingut efecte.
    void toggleShaders();
    auto toggleSupersampling() -> bool;  // false si GPU off / shaders off / actiu != POSTFX
    void toggleAspectRatio();
    void cycleScalingMode(int dir);  // Cicla DISABLED/STRETCH/LETTERBOX/OVERSCAN/INTEGER
    void toggleVSync();
    void cycleTextureFilter(int dir);        // Cicla NEAREST/LINEAR
    void changeInternalResolution(int dir);  // +/−1, clampat a [1, max_zoom_]
    auto nextShaderType() -> bool;           // false si GPU off / shaders off
    auto prevShaderType() -> bool;           // idem
    auto nextPreset() -> bool;               // false si GPU off / shaders off
    auto prevPreset() -> bool;               // idem
    [[nodiscard]] auto getCurrentPresetName() const -> const char*;
    void setActiveShader(Rendering::ShaderType type);
    void applyCurrentPostFXPreset();
    void applyCurrentCrtPiPreset();

    // Getters
    [[nodiscard]] auto isFullscreen() const -> bool { return fullscreen_; }
    [[nodiscard]] auto getZoom() const -> int { return zoom_; }
    [[nodiscard]] auto getMaxZoom() const -> int { return max_zoom_; }
    [[nodiscard]] auto isHardwareAccelerated() const -> bool;
    [[nodiscard]] auto getActiveShaderName() const -> const char*;
    [[nodiscard]] auto getWindow() -> SDL_Window* { return window_; }
    [[nodiscard]] auto getRenderer() -> SDL_Renderer* { return renderer_; }

#ifdef __EMSCRIPTEN__
    // Sincronització amb el canvas HTML quan el navegador canvia la mida
    // (fullscreen entrant/eixint, rotació de mòbil). Cridat pels callbacks
    // natius d'Emscripten registrats al constructor.
    void handleCanvasResized();
    void syncFullscreenFlagFromBrowser(bool is_fullscreen);
#endif

   private:
    Screen();

    void adjustWindowSize();
    void calculateMaxZoom();
    void initShaders();
    void applyFallbackPresentation();      // Logical presentation + scale mode per al path SDL_Renderer
    void ensureFallbackInternalTexture();  // Recrea internal_texture_sdl_ si cal (fallback path)

    static std::unique_ptr<Screen> instance;

    SDL_Window* window_{nullptr};
    SDL_Renderer* renderer_{nullptr};
    SDL_Texture* texture_{nullptr};               // 320x200 streaming, ABGR8888 (fallback SDL_Renderer)
    SDL_Texture* internal_texture_sdl_{nullptr};  // 320·N x 200·N TARGET (fallback path, només si N>1)
    int internal_texture_mult_{0};                // Multiplicador amb què es va crear internal_texture_sdl_

    // Backend GPU (nullptr si no disponible o desactivat)
    std::unique_ptr<Rendering::ShaderBackend> shader_backend_;

    void updateRenderInfo();

    struct FPS {
        Uint32 ticks{0};
        int frame_count{0};
        int last_value{0};

        void increment() { frame_count++; }
        auto calculate(Uint32 current_ticks) -> int {
            if (current_ticks - ticks >= 1000) {
                last_value = frame_count;
                frame_count = 0;
                ticks = current_ticks;
            }
            return last_value;
        }
    };

    int zoom_{3};
    int max_zoom_{6};
    bool fullscreen_{false};
    FPS fps_;
    std::string gpu_driver_;

    static constexpr int GAME_WIDTH = 320;
    static constexpr int GAME_HEIGHT = 200;
};
