#include "RenderManager.h"
#include <SDL3/SDL.h>
#include <algorithm>

RenderManager::RenderManager() = default;

RenderManager::~RenderManager() {
    if (m_initialized) {
        shutdown();
    }
}

bool RenderManager::initialize(int width, int height, const std::string& title) {
    if (m_initialized) {
        SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "RenderManager already initialized");
        return true;
    }

    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Initializing RenderManager (%dx%d)", width, height);

    // Create window
    m_window = SDL_CreateWindow(
        title.c_str(),
        width, height,
        SDL_WINDOW_RESIZABLE
    );

    if (!m_window) {
        SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create window: %s", SDL_GetError());
        return false;
    }

    // Create renderer
    m_renderer = SDL_CreateRenderer(m_window, nullptr);
    if (!m_renderer) {
        SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create renderer: %s", SDL_GetError());
        SDL_DestroyWindow(m_window);
        m_window = nullptr;
        return false;
    }

    // Enable VSync
    SDL_SetRenderVSync(m_renderer, 1);

    // Store window dimensions
    m_windowWidth = width;
    m_windowHeight = height;
    m_logicalWidth = width;
    m_logicalHeight = height;

    // Set initial logical size
    setLogicalSize(width, height);

    m_initialized = true;
    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager initialized successfully");
    return true;
}

void RenderManager::shutdown() {
    if (!m_initialized) {
        return;
    }

    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Shutting down RenderManager");

    if (m_renderer) {
        SDL_DestroyRenderer(m_renderer);
        m_renderer = nullptr;
    }

    if (m_window) {
        SDL_DestroyWindow(m_window);
        m_window = nullptr;
    }

    m_initialized = false;
    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager shutdown complete");
}

void RenderManager::beginFrame() {
    if (!m_initialized || !m_renderer) {
        return;
    }

    // Trace beginFrame entry
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame entry\n"); fclose(f); }
    }

    // Clear the screen (wrapped with trace)
    clear(12, 12, 16, 255); // Dark background similar to original

    // Trace after clear
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame after clear\n"); fclose(f); }
    }
}

void RenderManager::endFrame() {
    if (!m_initialized || !m_renderer) {
        return;
    }
    // Trace before present
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame before present\n"); fclose(f); }
    }

    SDL_RenderPresent(m_renderer);

    // Trace after present
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame after present\n"); fclose(f); }
    }
}

void RenderManager::setLogicalSize(int width, int height) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    m_logicalWidth = width;
    m_logicalHeight = height;
    updateLogicalScale();
}

void RenderManager::setViewport(int x, int y, int width, int height) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    SDL_Rect viewport = { x, y, width, height };
    SDL_SetRenderViewport(m_renderer, &viewport);
    // Keep cached logical viewport in sync if this matches our computed logical scale
    m_logicalVP = viewport;
}

void RenderManager::setScale(float scaleX, float scaleY) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    m_scaleX = scaleX;
    m_scaleY = scaleY;
    SDL_SetRenderScale(m_renderer, scaleX, scaleY);
}

void RenderManager::resetViewport() {
    if (!m_initialized || !m_renderer) {
        return;
    }

    // Reset to full window viewport and recompute logical scale/viewport
    SDL_SetRenderViewport(m_renderer, nullptr);
    updateLogicalScale();
}

// IRenderer interface implementation
void RenderManager::clearScreen(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
    clear(static_cast<Uint8>(r), static_cast<Uint8>(g), static_cast<Uint8>(b), static_cast<Uint8>(a));
}

void RenderManager::present() {
    if (!m_initialized || !m_renderer) {
        return;
    }
    
    SDL_RenderPresent(m_renderer);
}

SDL_Renderer* RenderManager::getSDLRenderer() {
    return m_renderer;
}

void RenderManager::getWindowDimensions(int& width, int& height) const {
    getWindowSize(width, height);
}

void RenderManager::setViewport(const SDL_Rect* viewport) {
    if (!m_initialized || !m_renderer) {
        return;
    }
    
    SDL_SetRenderViewport(m_renderer, viewport);
}

// Legacy clear method
void RenderManager::clear(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
    SDL_RenderClear(m_renderer);
}

void RenderManager::renderTexture(SDL_Texture* texture, const SDL_FRect* src, const SDL_FRect* dst) {
    if (!m_initialized || !m_renderer || !texture) {
        return;
    }

    // Trace renderTexture usage
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture entry tex=%llu src=%p dst=%p\n", (unsigned long long)(uintptr_t)texture, (void*)src, (void*)dst); fclose(f); }
    }
    SDL_RenderTexture(m_renderer, texture, src, dst);
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture after SDL_RenderTexture tex=%llu\n", (unsigned long long)(uintptr_t)texture); fclose(f); }
    }
}

void RenderManager::renderTexture(SDL_Texture* texture, float x, float y) {
    if (!texture) {
        return;
    }

    float w, h;
    SDL_GetTextureSize(texture, &w, &h);
    SDL_FRect dst = { x, y, w, h };
    renderTexture(texture, nullptr, &dst);
}

void RenderManager::renderTexture(SDL_Texture* texture, float x, float y, float w, float h) {
    if (!texture) {
        return;
    }

    SDL_FRect dst = { x, y, w, h };
    renderTexture(texture, nullptr, &dst);
}

void RenderManager::renderRect(const SDL_FRect& rect, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
    SDL_RenderFillRect(m_renderer, &rect);
}

void RenderManager::renderLine(float x1, float y1, float x2, float y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
    SDL_RenderLine(m_renderer, x1, y1, x2, y2);
}

void RenderManager::renderPoint(float x, float y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
    if (!m_initialized || !m_renderer) {
        return;
    }

    SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
    SDL_RenderPoint(m_renderer, x, y);
}

void RenderManager::handleWindowResize(int newWidth, int newHeight) {
    if (!m_initialized) {
        return;
    }

    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Window resized to %dx%d", newWidth, newHeight);
    
    m_windowWidth = newWidth;
    m_windowHeight = newHeight;
    updateLogicalScale();
}

void RenderManager::setFullscreen(bool fullscreen) {
    if (!m_initialized || !m_window) {
        return;
    }

    if (m_isFullscreen == fullscreen) {
        return;
    }

    SDL_SetWindowFullscreen(m_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
    m_isFullscreen = fullscreen;

    // Update window size after fullscreen change
    SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight);
    updateLogicalScale();
}

void RenderManager::getWindowSize(int& width, int& height) const {
    width = m_windowWidth;
    height = m_windowHeight;
}

void RenderManager::getTextureSize(SDL_Texture* tex, int& w, int& h) const {
    if (!tex) { w = 0; h = 0; return; }
    // SDL3 provides SDL_GetTextureSize which accepts float or int pointers depending on overloads
    float fw = 0.0f, fh = 0.0f;
    SDL_GetTextureSize(tex, &fw, &fh);
    w = int(fw + 0.5f);
    h = int(fh + 0.5f);
}

void RenderManager::updateLogicalScale() {
    if (!m_initialized || !m_renderer) {
        return;
    }

    // Calculate scale to fit logical size into window
    float scaleX = static_cast<float>(m_windowWidth) / static_cast<float>(m_logicalWidth);
    float scaleY = static_cast<float>(m_windowHeight) / static_cast<float>(m_logicalHeight);
    
    // Use uniform scaling to maintain aspect ratio
    float scale = std::min(scaleX, scaleY);
    if (scale <= 0.0f) {
        scale = 1.0f;
    }

    setScale(scale, scale);
    // Compute centered logical viewport that preserves aspect ratio and is centered in window
    int vpW = static_cast<int>(m_logicalWidth * scale);
    int vpH = static_cast<int>(m_logicalHeight * scale);
    int vpX = (m_windowWidth - vpW) / 2;
    int vpY = (m_windowHeight - vpH) / 2;
    SDL_Rect vp{ vpX, vpY, vpW, vpH };
    SDL_SetRenderViewport(m_renderer, &vp);

    // Cache logical viewport and scale for callers
    m_logicalVP = vp;
    m_logicalScale = scale;
}
