#include <ChunkWindow/ChunkWindow.hpp>
#include <GeneratorNode/GeneratorNode.hpp>
#include <Utilities/Utilities.hpp>
#include <WeatherManager/WeatherManager.hpp>
#include <World/World.hpp>

float WeatherManager::getCloudThreshold(float temperature)
{
    return std::clamp(
        expWithControlPoints(
            temperature,
            glm::vec2(283.15f, 0.2f),
            glm::vec2(303.15f, 0.5f)
        ),
        0.0f,
        1.0f
    );
}

WeatherManager::WeatherManager(World &world) : world(world)
{
    lastSpawnTicks = SDL_GetTicks();
}

void WeatherManager::init(GeneratorNode genNode)
{
    this->genNode = genNode;
    
    temperatureNoise.addLayer(genNode(0x74ad1b45f03ce246), 60 * 60, 4.0f);
    temperatureNoise.addLayer(genNode(0x7f3b1b45f03ce246), 60 * 60 * 12, 7.0f);

    humidityNoise.addLayer(genNode(0xafc0c5429ffefe15), 60 * 60, 1.0f);
    humidityNoise.addLayer(genNode(0xd5d0d85ac86feb86), 60 * 60 * 12, 1.0f);
}

float WeatherManager::getTemperature()
{
    uint64_t worldTime = world.getWorldTime();

    float perlinSample = temperatureNoise.sample(worldTime);
    float sinusoidSample = std::sin(world.getSolarAngle());

    return 293.15f + 10.0f * sinusoidSample + perlinSample;
}

float WeatherManager::getHumidity()
{
    uint64_t worldTime = world.getWorldTime();

    float perlinSample = humidityNoise.sample(worldTime);

    return std::min(0.33f * (perlinSample + 2.0f), 1.0f);
}

float WeatherManager::getCloudCover()
{
    float temperature = getTemperature();
    float humidity = getHumidity();
    float threshold = getCloudThreshold(temperature);

    if (humidity < threshold)
    {
        return 0.0f;
    }

    return (humidity - threshold) / (1.0f - threshold);
}

float WeatherManager::getRainIntensity()
{
    float cloudCover = getCloudCover();
    if (cloudCover < 0.5f)
    {
        return 0.0f;
    }

    return 2.0f * (cloudCover - 0.5f);
}

void WeatherManager::applyEffects(ChunkWindow &window)
{
    float rainIntensity = getRainIntensity();

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> normDis(0, 1);
    std::uniform_real_distribution<float> unitDis(-1, 1);

    Uint32 ticksPassed = SDL_GetTicks() - lastSpawnTicks;
    lastSpawnTicks = SDL_GetTicks();

    float secondsPassed = (float) ticksPassed / 1000.0f;

    for (int i = 0; i < topParticlesPerSecond * rainIntensity * secondsPassed; i++)
    {
        ParticlesStage::Particle particle{};
        float dist = 1.0f + (normDis(gen) * 29.0f);
        float angle = 2.0f * 3.1415f * normDis(gen);

        float dx = std::sin(angle) * dist;
        float dy = std::cos(angle) * dist;
        particle.startPos = glm::vec3(dx, dy, 0.0);
        particle.startPos.z = 32.0f;
        particle.startVel = glm::vec3(0.0f, 0.0f, -4.0f);
        particle.startSize = glm::vec3(1.0f/64.0f, 1.0f/64.0f, 15.0f/64.0f);
        particle.endSize = particle.startSize;
        particle.startColor = glm::vec4(0.1f, 0.3f, 0.8f, 0.3f);
        particle.endColor = glm::vec4(0.1f, 0.3f, 0.8f, 0.3f);
        particle.startTime = SDL_GetTicks();
        particle.endTime = SDL_GetTicks() + 800;

        window.emitParticle(particle);
    }
}