//
// Created by sfd on 25-5-21.
//

#include "ParticleSystem.h"

#include <random>

#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtx/compatibility.hpp"


class Random
{
public:
    static void Init()
    {
        s_RandEngine.seed(std::random_device()());
    }
    static float Float()
    {
        return (float)s_Distribution(s_RandEngine) / (float)std::numeric_limits<uint32_t>::max();
    }
private:
    static std::mt19937 s_RandEngine;
    static std::uniform_int_distribution<std::mt19937::result_type> s_Distribution;
};


std::mt19937 Random::s_RandEngine;
std::uniform_int_distribution<std::mt19937::result_type> Random::s_Distribution;




ParticleSystem::ParticleSystem()
{
    m_particlePool.resize(m_PoolIndex--);
}

void ParticleSystem::OnUpdate(Hazel::TimeStep& ts)
{
    for (auto& particle : m_particlePool)
    {
        if (!particle.Active)
            continue;
        if (particle.LifeRemaing <= 0.0f)
        {
            particle.Active = false;
            continue;
        }
        particle.LifeRemaing -= ts;
        particle.Position += particle.Velocity * static_cast<float>(ts);
        particle.Rotation += 0.01f * ts;
    }
}

void ParticleSystem::OnRender(Hazel::OrthographicCamera& camera)
{
    bool isParticule = false;
    for (auto& particle : m_particlePool)
    {
        if (particle.Active)
        {
            isParticule = true;
            break;
        }
    }

    if (isParticule)
    {
        Hazel::Renderer2D::BeginScene(camera);

        for (auto& particle : m_particlePool)
        {
            if (!particle.Active)
                continue;
            float     life  = particle.LifeRemaing / particle.LifeTime;
            glm::vec4 color = glm::lerp(particle.ColorEnd, particle.ColorBegin, life);

            float size = glm::lerp(particle.SizeEnd, particle.SizeBegin, life);

            Hazel::Renderer2D::DrawRotateQuad(particle.Position, {size, size}, particle.Rotation, color);
        }

        Hazel::Renderer2D::EndScene();
    }
}

void ParticleSystem::Emit(const ParticleProp& particuleProps)
{
    Particle& particle = m_particlePool[m_PoolIndex];
    particle.Active = true;
    particle.Position = particuleProps.Position;
    particle.Rotation = Random::Float() * 2.0f * glm::pi<float>();

    // Velocity
    particle.Velocity = particuleProps.Velocity;
    particle.Velocity.x += particuleProps.VelocityVariation.x * (Random::Float() - 0.5f);
    particle.Velocity.y += particuleProps.VelocityVariation.y * (Random::Float() - 0.5f);

    // Color
    particle.ColorBegin = particuleProps.ColorBegin;
    particle.ColorEnd = particuleProps.ColorEnd;

    particle.LifeTime = particuleProps.LifeTime;
    particle.LifeRemaing = particuleProps.LifeTime;
    particle.SizeBegin = particuleProps.SizeBegin + particuleProps.SizeVariation * (Random::Float() - 0.5f);
    particle.SizeEnd = particuleProps.SizeEnd;

    m_PoolIndex = --m_PoolIndex % m_particlePool.size();
}
