#include "GlobalState.h"
#include "Config.h"
#include <SDL3/SDL.h>
#include <algorithm>
#include <random>
#include <cmath>

namespace {
constexpr float PI_F = 3.14159265358979323846f;

float randRange(float minVal, float maxVal) {
    return minVal + (static_cast<float>(rand()) / static_cast<float>(RAND_MAX)) * (maxVal - minVal);
}

SDL_Color randomFireworkColor() {
    static const SDL_Color palette[] = {
        {255, 120, 80, 255},
        {255, 190, 60, 255},
        {120, 210, 255, 255},
        {170, 120, 255, 255},
        {255, 90, 180, 255},
        {120, 255, 170, 255},
        {255, 255, 180, 255}
    };
    size_t idx = static_cast<size_t>(rand() % (sizeof(palette) / sizeof(palette[0])));
    return palette[idx];
}

SDL_Color scaleColor(SDL_Color color, float factor, Uint8 alphaOverride = 0) {
    auto clampChannel = [](float value) -> Uint8 {
        return static_cast<Uint8>(std::max(0.0f, std::min(255.0f, std::round(value))));
    };
    SDL_Color result;
    result.r = clampChannel(color.r * factor);
    result.g = clampChannel(color.g * factor);
    result.b = clampChannel(color.b * factor);
    result.a = alphaOverride ? alphaOverride : color.a;
    return result;
}

SDL_Color mixColors(SDL_Color a, SDL_Color b, float t) {
    t = std::clamp(t, 0.0f, 1.0f);
    auto lerpChannel = [t](Uint8 ca, Uint8 cb) -> Uint8 {
        float blended = ca + (cb - ca) * t;
        return static_cast<Uint8>(std::max(0.0f, std::min(255.0f, blended)));
    };
    SDL_Color result;
    result.r = lerpChannel(a.r, b.r);
    result.g = lerpChannel(a.g, b.g);
    result.b = lerpChannel(a.b, b.b);
    result.a = lerpChannel(a.a, b.a);
    return result;
}
}

GlobalState& GlobalState::instance() {
    static GlobalState instance;
    return instance;
}

void GlobalState::initialize() {
    if (m_initialized) {
        return;
    }

    // Initialize timing
    lastMs = SDL_GetTicks();
    loadStart = SDL_GetTicks();

    // Initialize viewport to logical dimensions
    logicalVP = {0, 0, Config::Logical::WIDTH, Config::Logical::HEIGHT};

    // Initialize fireworks system
    fireworks.clear();
    lastFireworkTime = 0;

    m_initialized = true;
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[GlobalState] Initialized");
}

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

    // Clear fireworks
    fireworks.clear();

    m_initialized = false;
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[GlobalState] Shutdown complete");
}

namespace {
using Firework = GlobalState::TetrisFirework;
using BlockParticle = GlobalState::BlockParticle;
using SparkParticle = GlobalState::SparkParticle;
void spawnSparks(Firework& firework, float cx, float cy, SDL_Color baseColor, float speedBase) {
    int sparkCount = 10 + (rand() % 10);
    for (int i = 0; i < sparkCount; ++i) {
        SparkParticle spark;
        spark.x = cx;
        spark.y = cy;
        float angle = randRange(0.0f, PI_F * 2.0f);
        float speed = speedBase * randRange(1.05f, 1.6f);
        spark.vx = std::cos(angle) * speed;
        spark.vy = std::sin(angle) * speed - randRange(30.0f, 90.0f);
        spark.life = 0.0f;
        spark.maxLife = 260.0f + randRange(0.0f, 200.0f);
        spark.thickness = randRange(0.8f, 2.2f);
        spark.color = scaleColor(baseColor, randRange(0.85f, 1.2f), 255);
        firework.sparks.push_back(spark);
    }
}

void triggerFireworkBurst(Firework& firework, int burstIndex) {
    SDL_Color burstColor = firework.burstColors[burstIndex % firework.burstColors.size()];
    float centerX = firework.originX + randRange(-30.0f, 30.0f);
    float centerY = firework.originY - burstIndex * randRange(14.0f, 24.0f) + randRange(-10.0f, 10.0f);
    int particleCount = 22 + (rand() % 16);
    float speedBase = 90.0f + burstIndex * 40.0f;

    for (int i = 0; i < particleCount; ++i) {
        BlockParticle particle;
        particle.x = centerX;
        particle.y = centerY;
        float angle = randRange(0.0f, PI_F * 2.0f);
        float speed = speedBase + randRange(-20.0f, 70.0f);
        particle.vx = std::cos(angle) * speed;
        particle.vy = std::sin(angle) * speed - randRange(35.0f, 95.0f);
        particle.maxLife = 950.0f + randRange(0.0f, 420.0f) + burstIndex * 220.0f;
        particle.life = particle.maxLife;
        particle.crackle = (rand() % 100) < 75;
        particle.flickerSeed = randRange(0.0f, PI_F * 2.0f);
        if (particle.crackle) {
            particle.size = randRange(1.4f, 3.2f);
        } else {
            particle.size = 3.2f + randRange(0.0f, 2.6f) + burstIndex * 0.6f;
        }
        particle.color = scaleColor(burstColor, randRange(0.85f, 1.2f));
        particle.dualColor = (rand() % 100) < 55;
        if (particle.dualColor) {
            SDL_Color alt = randomFireworkColor();
            float luminanceDiff = std::abs(static_cast<float>(alt.r + alt.g + alt.b) - (particle.color.r + particle.color.g + particle.color.b));
            if (luminanceDiff < 40.0f) {
                alt = scaleColor(particle.color, randRange(0.6f, 1.4f));
            }
            particle.accentColor = alt;
            particle.colorBlendSpeed = randRange(0.6f, 1.4f);
        } else {
            particle.accentColor = particle.color;
            particle.colorBlendSpeed = 1.0f;
        }
        firework.particles.push_back(particle);
    }

    spawnSparks(firework, centerX, centerY, burstColor, speedBase);
}
}

void GlobalState::updateFireworks(double frameMs) {
    if (frameMs <= 0.0) {
        frameMs = 16.0;
    }

    const Uint64 currentTime = SDL_GetTicks();
    size_t activeCount = 0;
    for (const auto& fw : fireworks) {
        if (fw.active) {
            ++activeCount;
        }
    }

    constexpr size_t MAX_SIMULTANEOUS_FIREWORKS = 2;
    bool canSpawnNew = activeCount < MAX_SIMULTANEOUS_FIREWORKS;
    bool spawnedFirework = false;

    if (canSpawnNew) {
        Uint64 interval = 1300 + static_cast<Uint64>(rand() % 1400);
        if (currentTime - lastFireworkTime > interval) {
            float x = Config::Logical::WIDTH * (0.15f + randRange(0.0f, 0.70f));
            float y = Config::Logical::HEIGHT * (0.18f + randRange(0.0f, 0.40f));
            createFirework(x, y);
            lastFireworkTime = currentTime;
            lastFireworkX = x;
            lastFireworkY = y;
            pendingStaggerFirework = (rand() % 100) < 65;
            if (pendingStaggerFirework) {
                nextStaggerFireworkTime = currentTime + 250 + static_cast<Uint64>(rand() % 420);
            }
            spawnedFirework = true;
        }
    }

    if (!spawnedFirework && pendingStaggerFirework && canSpawnNew && currentTime >= nextStaggerFireworkTime) {
        float x = lastFireworkX + randRange(-140.0f, 140.0f);
        float y = lastFireworkY + randRange(-80.0f, 50.0f);
        x = std::clamp(x, Config::Logical::WIDTH * 0.10f, Config::Logical::WIDTH * 0.90f);
        y = std::clamp(y, Config::Logical::HEIGHT * 0.15f, Config::Logical::HEIGHT * 0.70f);
        createFirework(x, y);
        lastFireworkTime = currentTime;
        lastFireworkX = x;
        lastFireworkY = y;
        pendingStaggerFirework = false;
        spawnedFirework = true;
    }

    const float dtSeconds = static_cast<float>(frameMs / 1000.0);
    const float deltaMs = static_cast<float>(frameMs);

    for (auto& firework : fireworks) {
        if (!firework.active) {
            continue;
        }

        firework.elapsedMs += deltaMs;
        while (firework.nextBurst < static_cast<int>(firework.burstSchedule.size()) &&
               firework.elapsedMs >= firework.burstSchedule[firework.nextBurst]) {
            triggerFireworkBurst(firework, firework.nextBurst);
            firework.nextBurst++;
        }

        for (auto it = firework.particles.begin(); it != firework.particles.end();) {
            it->life -= deltaMs;
            if (it->life <= 0.0f) {
                it = firework.particles.erase(it);
                continue;
            }
            it->x += it->vx * dtSeconds;
            it->y += it->vy * dtSeconds;
            it->vx *= 0.986f;
            it->vy = it->vy * 0.972f + 70.0f * dtSeconds;
            ++it;
        }

        for (auto it = firework.sparks.begin(); it != firework.sparks.end();) {
            it->life += deltaMs;
            if (it->life >= it->maxLife) {
                it = firework.sparks.erase(it);
                continue;
            }
            it->x += it->vx * dtSeconds;
            it->y += it->vy * dtSeconds;
            it->vx *= 0.992f;
            it->vy = it->vy * 0.965f + 120.0f * dtSeconds;
            ++it;
        }

        bool pendingBursts = firework.nextBurst < static_cast<int>(firework.burstSchedule.size());
        firework.active = pendingBursts || !firework.particles.empty() || !firework.sparks.empty();
    }
}

void GlobalState::createFirework(float x, float y) {
    // Find an inactive firework to reuse
    TetrisFirework* firework = nullptr;
    for (auto& fw : fireworks) {
        if (!fw.active) {
            firework = &fw;
            break;
        }
    }

    // If no inactive firework found, create a new one
    if (!firework) {
        fireworks.emplace_back();
        firework = &fireworks.back();
    }

    firework->active = true;
    firework->particles.clear();
    firework->sparks.clear();
    firework->originX = x;
    firework->originY = y;
    firework->elapsedMs = 0.0f;
    firework->nextBurst = 0;
    firework->burstSchedule = {
        0.0f,
        220.0f + randRange(0.0f, 160.0f),
        420.0f + randRange(0.0f, 260.0f)
    };

    SDL_Color baseColor = randomFireworkColor();
    for (int i = 0; i < 3; ++i) {
        float wobble = randRange(-0.08f, 0.08f);
        firework->burstColors[i] = scaleColor(baseColor, 1.0f - i * 0.12f + wobble, 255);
    }
}

void GlobalState::drawFireworks(SDL_Renderer* renderer, SDL_Texture* blocksTex) {
    (void)blocksTex;

    auto renderCircle = [renderer](float cx, float cy, float radius) {
        int ir = static_cast<int>(std::ceil(radius));
        for (int dy = -ir; dy <= ir; ++dy) {
            float row = std::sqrt(std::max(0.0f, radius * radius - static_cast<float>(dy * dy)));
            SDL_FRect line{cx - row, cy + dy, row * 2.0f, 1.0f};
            SDL_RenderFillRect(renderer, &line);
        }
    };

    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);
    for (const auto& firework : fireworks) {
        if (!firework.active) continue;

        for (const auto& spark : firework.sparks) {
            if (spark.life >= spark.maxLife) continue;
            float progress = spark.life / spark.maxLife;
            Uint8 alpha = static_cast<Uint8>((1.0f - progress) * 255.0f);
            if (alpha == 0) continue;
            SDL_SetRenderDrawColor(renderer, spark.color.r, spark.color.g, spark.color.b, alpha);
            float trailScale = 0.015f * spark.thickness;
            float tailX = spark.x - spark.vx * trailScale;
            float tailY = spark.y - spark.vy * trailScale;
            SDL_RenderLine(renderer,
                           static_cast<int>(spark.x),
                           static_cast<int>(spark.y),
                           static_cast<int>(tailX),
                           static_cast<int>(tailY));
        }
    }

    auto sampleParticleColor = [](const BlockParticle& particle) -> SDL_Color {
        if (!particle.dualColor) {
            return particle.color;
        }
        float elapsed = particle.maxLife - particle.life;
        float phase = particle.flickerSeed * 1.8f + elapsed * 0.0025f * particle.colorBlendSpeed;
        float mixFactor = 0.5f + 0.5f * std::sin(phase);
        return mixColors(particle.color, particle.accentColor, mixFactor);
    };

    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
    for (const auto& firework : fireworks) {
        if (!firework.active) continue;

        for (const auto& particle : firework.particles) {
            if (particle.life <= 0.0f) continue;
            float lifeRatio = particle.life / particle.maxLife;
            float alphaF = std::pow(std::max(0.0f, lifeRatio), 0.75f);
            if (particle.crackle) {
                SDL_Color dynamicColor = sampleParticleColor(particle);
                float flicker = 0.55f + 0.45f * std::sin(particle.flickerSeed + particle.life * 0.018f);
                Uint8 alpha = static_cast<Uint8>(alphaF * flicker * 255.0f);
                if (alpha == 0) continue;
                SDL_SetRenderDrawColor(renderer, dynamicColor.r, dynamicColor.g, dynamicColor.b, alpha);
                float stretch = particle.size * (2.5f + (1.0f - lifeRatio) * 1.3f);
                float angle = particle.flickerSeed * 3.0f + particle.life * 0.004f;
                float dx = std::cos(angle) * stretch;
                float dy = std::sin(angle) * stretch * 0.7f;
                SDL_RenderLine(renderer,
                               static_cast<int>(particle.x - dx),
                               static_cast<int>(particle.y - dy),
                               static_cast<int>(particle.x + dx),
                               static_cast<int>(particle.y + dy));
                SDL_RenderLine(renderer,
                               static_cast<int>(particle.x - dy * 0.45f),
                               static_cast<int>(particle.y + dx * 0.45f),
                               static_cast<int>(particle.x + dy * 0.45f),
                               static_cast<int>(particle.y - dx * 0.45f));
            } else {
                SDL_Color dynamicColor = sampleParticleColor(particle);
                Uint8 alpha = static_cast<Uint8>(alphaF * 255.0f);
                SDL_SetRenderDrawColor(renderer, dynamicColor.r, dynamicColor.g, dynamicColor.b, alpha);
                float radius = particle.size * (0.5f + 0.3f * lifeRatio);
                renderCircle(particle.x, particle.y, radius);
            }
        }
    }

    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
}

void GlobalState::resetGameState() {
    // Reset game-related state
    leftHeld = false;
    rightHeld = false;
    moveTimerMs = 0.0;
    startLevelSelection = 0;
}

void GlobalState::resetUIState() {
    // Reset UI state
    showSettingsPopup = false;
    showExitConfirmPopup = false;
    hoveredButton = -1;
}

void GlobalState::resetAnimationState() {
    // Reset animation state
    logoAnimCounter = 0.0;
    fireworks.clear();
    lastFireworkTime = 0;
}

void GlobalState::updateLogicalDimensions(int windowWidth, int windowHeight) {
    // For now, keep logical dimensions proportional to window size
    // You can adjust this logic based on your specific needs
    
    // Option 1: Keep fixed aspect ratio and scale uniformly
    const float targetAspect = static_cast<float>(Config::Logical::WIDTH) / static_cast<float>(Config::Logical::HEIGHT);
    const float windowAspect = static_cast<float>(windowWidth) / static_cast<float>(windowHeight);
    
    if (windowAspect > targetAspect) {
        // Window is wider than target aspect - fit to height
        currentLogicalHeight = Config::Logical::HEIGHT;
        currentLogicalWidth = static_cast<int>(currentLogicalHeight * windowAspect);
    } else {
        // Window is taller than target aspect - fit to width  
        currentLogicalWidth = Config::Logical::WIDTH;
        currentLogicalHeight = static_cast<int>(currentLogicalWidth / windowAspect);
    }
    
    // Ensure minimum sizes
    currentLogicalWidth = std::max(currentLogicalWidth, 800);
    currentLogicalHeight = std::max(currentLogicalHeight, 600);
    
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, 
        "[GlobalState] Updated logical dimensions: %dx%d (window: %dx%d)", 
        currentLogicalWidth, currentLogicalHeight, windowWidth, windowHeight);
}
