#include "ParticleComponent.h"
#include "../Renderer.h"
#include <random>
#include <algorithm>

using namespace core::types;

static std::mt19937 rng{std::random_device{}()};

static float randRange(float a, float b)
{
    return std::uniform_real_distribution<float>(a, b)(rng);
}

void ParticleComponent::SetEmitterSettings(const ParticleEmitterSettings &s)
{
    settings = s;
}

void ParticleComponent::Emit()
{
    for (int i = 0; i < settings.maxParticles; ++i)
        SpawnParticle();
}

void ParticleComponent::Update(float dt)
{
    emitAccumulator += dt;
    float emitInterval = 1.0f / settings.emissionRate;

    if (!settings.burst)
    {
        while (emitAccumulator >= emitInterval)
        {
            if ((int)particles.size() < settings.maxParticles)
                SpawnParticle();
            emitAccumulator -= emitInterval;
        }
    }

    for (auto &p : particles)
    {
        p.life -= dt;
        if (p.life > 0)
        {
            p.position += p.velocity * dt;
            p.rotation += dt * 2.0f;
        }
    }

    particles.erase(
        std::remove_if(particles.begin(), particles.end(),
                       [](const Particle &p)
                       { return p.life <= 0.0f; }),
        particles.end());
}

void ParticleComponent::SpawnParticle()
{
    Particle p;
    p.life = randRange(settings.lifeMin, settings.lifeMax);
    p.size = randRange(settings.sizeMin, settings.sizeMax);

    float angle = std::atan2(settings.direction.y, settings.direction.x) +
                  randRange(-settings.spread * 0.5f, settings.spread * 0.5f);
    float speed = randRange(settings.speedMin, settings.speedMax);
    p.velocity = Vec2(std::cos(angle), std::sin(angle)) * speed;

    p.position = owner ? core::types::Vec2(owner->GetWorldPosition()) : core::types::Vec2(0.0f, 0.0f);


    p.rotation = randRange(0.0f, 6.28318f); // 2pi
    p.color = settings.startColor;

    particles.push_back(p);
}

void ParticleComponent::Save(YAML::Emitter &out) const
{
    out << YAML::BeginMap;
    out << YAML::Key << "type" << YAML::Value << "ParticleComponent";

    out << YAML::Key << "maxParticles" << YAML::Value << settings.maxParticles;
    out << YAML::Key << "emissionRate" << YAML::Value << settings.emissionRate;
    out << YAML::Key << "lifeMin" << YAML::Value << settings.lifeMin;
    out << YAML::Key << "lifeMax" << YAML::Value << settings.lifeMax;
    out << YAML::Key << "sizeMin" << YAML::Value << settings.sizeMin;
    out << YAML::Key << "sizeMax" << YAML::Value << settings.sizeMax;
    out << YAML::Key << "speedMin" << YAML::Value << settings.speedMin;
    out << YAML::Key << "speedMax" << YAML::Value << settings.speedMax;

    out << YAML::Key << "direction" << YAML::Value << YAML::Flow << YAML::BeginSeq << settings.direction.x << settings.direction.y << YAML::EndSeq;
    out << YAML::Key << "spread" << YAML::Value << settings.spread;

    auto writeColor = [](const Color &c, YAML::Emitter &out)
    {
        out << YAML::Flow << YAML::BeginSeq << c.r << c.g << c.b << c.a << YAML::EndSeq;
    };
    out << YAML::Key << "startColor" << YAML::Value;
    writeColor(settings.startColor, out);
    out << YAML::Key << "endColor" << YAML::Value;
    writeColor(settings.endColor, out);

    out << YAML::Key << "loop" << YAML::Value << settings.loop;
    out << YAML::Key << "burst" << YAML::Value << settings.burst;

    out << YAML::Key << "roundness" << YAML::Value << settings.roundness;

    out << YAML::EndMap;
}

void ParticleComponent::Load(const YAML::Node &node)
{
    if (!node || !node.IsMap())
        return;

    auto vec2 = [](const YAML::Node &n)
    {
        return Vec2(n[0].as<float>(), n[1].as<float>());
    };

    auto color = [](const YAML::Node &n)
    {
        return Color(n[0].as<float>(), n[1].as<float>(), n[2].as<float>(), n[3].as<float>());
    };

    settings.maxParticles = node["maxParticles"].as<int>(settings.maxParticles);
    settings.emissionRate = node["emissionRate"].as<float>(settings.emissionRate);
    settings.lifeMin = node["lifeMin"].as<float>(settings.lifeMin);
    settings.lifeMax = node["lifeMax"].as<float>(settings.lifeMax);
    settings.sizeMin = node["sizeMin"].as<float>(settings.sizeMin);
    settings.sizeMax = node["sizeMax"].as<float>(settings.sizeMax);
    settings.speedMin = node["speedMin"].as<float>(settings.speedMin);
    settings.speedMax = node["speedMax"].as<float>(settings.speedMax);

    if (node["direction"])
        settings.direction = vec2(node["direction"]);
    if (node["spread"])
        settings.spread = node["spread"].as<float>();
    if (node["startColor"])
        settings.startColor = color(node["startColor"]);
    if (node["endColor"])
        settings.endColor = color(node["endColor"]);
    if (node["loop"])
        settings.loop = node["loop"].as<bool>();
    if (node["burst"])
        settings.burst = node["burst"].as<bool>();
    if (node["roundness"])
        settings.roundness = node["roundness"].as<float>();
}
