#include "engine/particle.h"
#include "engine/renderer.h"
#include "engine/physics.h"
#include "engine/camera.h"
#include <stdlib.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

static Particle s_particles[MAX_PARTICLES];

/* ── Helpers ─────────────────────────────────────────── */

static float randf(void) {
    return (float)rand() / (float)RAND_MAX;
}

static float randf_range(float lo, float hi) {
    return lo + randf() * (hi - lo);
}

static uint8_t clamp_u8(int v) {
    if (v < 0)   return 0;
    if (v > 255) return 255;
    return (uint8_t)v;
}

/* ── Core API ────────────────────────────────────────── */

void particle_init(void) {
    for (int i = 0; i < MAX_PARTICLES; i++) {
        s_particles[i].active = false;
    }
}

static Particle *alloc_particle(void) {
    /* Find a free slot */
    for (int i = 0; i < MAX_PARTICLES; i++) {
        if (!s_particles[i].active) return &s_particles[i];
    }
    /* Steal the oldest (lowest remaining life) */
    float min_life = 1e9f;
    int   min_idx  = 0;
    for (int i = 0; i < MAX_PARTICLES; i++) {
        if (s_particles[i].life < min_life) {
            min_life = s_particles[i].life;
            min_idx  = i;
        }
    }
    return &s_particles[min_idx];
}

void particle_emit(const ParticleBurst *b) {
    for (int i = 0; i < b->count; i++) {
        Particle *p = alloc_particle();

        /* Random angle within spread cone */
        float angle = b->direction + randf_range(-b->spread, b->spread);
        float speed = randf_range(b->speed_min, b->speed_max);

        p->pos   = b->origin;
        p->vel   = vec2(cosf(angle) * speed, sinf(angle) * speed);
        p->life  = randf_range(b->life_min, b->life_max);
        p->max_life = p->life;
        p->size  = randf_range(b->size_min, b->size_max);
        p->drag  = b->drag;
        p->gravity_scale = b->gravity_scale;
        p->active = true;

        /* Color with optional variation */
        p->color = b->color;
        if (b->color_vary) {
            int vary = 30;
            p->color.r = clamp_u8(b->color.r + (int)randf_range(-vary, vary));
            p->color.g = clamp_u8(b->color.g + (int)randf_range(-vary, vary));
            p->color.b = clamp_u8(b->color.b + (int)randf_range(-vary, vary));
        }
    }
}

void particle_update(float dt) {
    float gravity = physics_get_gravity();
    float wind    = physics_get_wind();

    for (int i = 0; i < MAX_PARTICLES; i++) {
        Particle *p = &s_particles[i];
        if (!p->active) continue;

        p->life -= dt;
        if (p->life <= 0) {
            p->active = false;
            continue;
        }

        /* Apply gravity */
        p->vel.y += gravity * p->gravity_scale * dt;

        /* Apply wind (reuse gravity_scale as environmental-force scale) */
        if (wind != 0.0f && p->gravity_scale > 0.0f) {
            p->vel.x += wind * p->gravity_scale * dt;
        }

        /* Apply drag */
        if (p->drag > 0) {
            float factor = 1.0f - p->drag * dt;
            if (factor < 0) factor = 0;
            p->vel.x *= factor;
            p->vel.y *= factor;
        }

        /* Move */
        p->pos.x += p->vel.x * dt;
        p->pos.y += p->vel.y * dt;
    }
}

void particle_render(const Camera *cam) {
    for (int i = 0; i < MAX_PARTICLES; i++) {
        Particle *p = &s_particles[i];
        if (!p->active) continue;

        /* Alpha fade based on remaining life */
        float t = p->life / p->max_life;
        SDL_Color c = p->color;
        c.a = (uint8_t)(t * 255.0f);

        /* Shrink slightly as particle ages */
        float sz = p->size * (0.3f + 0.7f * t);
        if (sz < 0.5f) sz = 0.5f;

        renderer_draw_rect(p->pos, vec2(sz, sz), c, LAYER_PARTICLES, cam);
    }
}

void particle_clear(void) {
    for (int i = 0; i < MAX_PARTICLES; i++) {
        s_particles[i].active = false;
    }
}

/* ── Presets ─────────────────────────────────────────── */

void particle_emit_death_puff(Vec2 pos, SDL_Color color) {
    ParticleBurst b = {
        .origin    = pos,
        .count     = 12,
        .speed_min = 30.0f,
        .speed_max = 100.0f,
        .life_min  = 0.2f,
        .life_max  = 0.5f,
        .size_min  = 1.5f,
        .size_max  = 3.5f,
        .spread    = (float)M_PI,  /* full circle */
        .direction = 0,
        .drag      = 3.0f,
        .gravity_scale = 0.3f,
        .color     = color,
        .color_vary = true,
    };
    particle_emit(&b);
}

void particle_emit_landing_dust(Vec2 pos) {
    ParticleBurst b = {
        .origin    = pos,
        .count     = 6,
        .speed_min = 20.0f,
        .speed_max = 60.0f,
        .life_min  = 0.15f,
        .life_max  = 0.35f,
        .size_min  = 1.0f,
        .size_max  = 2.5f,
        .spread    = 0.6f,                           /* ~35 degrees */
        .direction = -(float)M_PI * 0.5f,            /* upward     */
        .drag      = 4.0f,
        .gravity_scale = 0.0f,
        .color     = {180, 170, 150, 255},            /* dusty tan  */
        .color_vary = true,
    };
    particle_emit(&b);
}

void particle_emit_spark(Vec2 pos, SDL_Color color) {
    ParticleBurst b = {
        .origin    = pos,
        .count     = 5,
        .speed_min = 40.0f,
        .speed_max = 120.0f,
        .life_min  = 0.1f,
        .life_max  = 0.25f,
        .size_min  = 1.0f,
        .size_max  = 2.0f,
        .spread    = (float)M_PI,
        .direction = 0,
        .drag      = 2.0f,
        .gravity_scale = 0.5f,
        .color     = color,
        .color_vary = true,
    };
    particle_emit(&b);
}

void particle_emit_jetpack_burst(Vec2 pos, Vec2 dash_dir) {
    /* Exhaust fires opposite to dash direction */
    float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);

    /* Big initial burst — hot core (white/yellow) */
    ParticleBurst core = {
        .origin    = pos,
        .count     = 18,
        .speed_min = 80.0f,
        .speed_max = 200.0f,
        .life_min  = 0.15f,
        .life_max  = 0.35f,
        .size_min  = 2.0f,
        .size_max  = 4.0f,
        .spread    = 0.5f,              /* ~30 degree cone */
        .direction = exhaust_angle,
        .drag      = 3.0f,
        .gravity_scale = 0.1f,
        .color     = {255, 220, 120, 255},  /* bright yellow */
        .color_vary = true,
    };
    particle_emit(&core);

    /* Outer plume — orange/red, wider spread */
    ParticleBurst plume = {
        .origin    = pos,
        .count     = 14,
        .speed_min = 50.0f,
        .speed_max = 150.0f,
        .life_min  = 0.2f,
        .life_max  = 0.45f,
        .size_min  = 1.5f,
        .size_max  = 3.5f,
        .spread    = 0.8f,              /* wider cone */
        .direction = exhaust_angle,
        .drag      = 2.5f,
        .gravity_scale = 0.2f,
        .color     = {255, 120, 40, 255},   /* orange */
        .color_vary = true,
    };
    particle_emit(&plume);

    /* Smoke tail — grey, slow, lingers */
    ParticleBurst smoke = {
        .origin    = pos,
        .count     = 8,
        .speed_min = 20.0f,
        .speed_max = 60.0f,
        .life_min  = 0.3f,
        .life_max  = 0.6f,
        .size_min  = 2.0f,
        .size_max  = 4.5f,
        .spread    = 1.0f,
        .direction = exhaust_angle,
        .drag      = 4.0f,
        .gravity_scale = -0.1f,         /* slight float-up */
        .color     = {160, 150, 140, 200},  /* smoke grey */
        .color_vary = true,
    };
    particle_emit(&smoke);
}

void particle_emit_jetpack_trail(Vec2 pos, Vec2 dash_dir) {
    /* Continuous trail — fewer particles per frame, same exhaust direction */
    float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);

    /* Hot sparks */
    ParticleBurst sparks = {
        .origin    = pos,
        .count     = 3,
        .speed_min = 60.0f,
        .speed_max = 160.0f,
        .life_min  = 0.1f,
        .life_max  = 0.25f,
        .size_min  = 1.5f,
        .size_max  = 3.0f,
        .spread    = 0.4f,
        .direction = exhaust_angle,
        .drag      = 3.0f,
        .gravity_scale = 0.1f,
        .color     = {255, 200, 80, 255},   /* yellow-orange */
        .color_vary = true,
    };
    particle_emit(&sparks);

    /* Smoke wisps */
    ParticleBurst smoke = {
        .origin    = pos,
        .count     = 2,
        .speed_min = 15.0f,
        .speed_max = 40.0f,
        .life_min  = 0.2f,
        .life_max  = 0.4f,
        .size_min  = 1.5f,
        .size_max  = 3.5f,
        .spread    = 0.7f,
        .direction = exhaust_angle,
        .drag      = 3.5f,
        .gravity_scale = -0.1f,
        .color     = {140, 130, 120, 180},
        .color_vary = true,
    };
    particle_emit(&smoke);
}

void particle_emit_jetpack_boost_burst(Vec2 pos, Vec2 dash_dir) {
    /* Blue flame accents mixed into the regular jetpack burst */
    float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);

    /* Hot blue core — bright electric blue */
    ParticleBurst core = {
        .origin    = pos,
        .count     = 10,
        .speed_min = 90.0f,
        .speed_max = 220.0f,
        .life_min  = 0.15f,
        .life_max  = 0.35f,
        .size_min  = 2.0f,
        .size_max  = 4.5f,
        .spread    = 0.4f,
        .direction = exhaust_angle,
        .drag      = 2.5f,
        .gravity_scale = 0.05f,
        .color     = {80, 160, 255, 255},   /* electric blue */
        .color_vary = true,
    };
    particle_emit(&core);

    /* Outer blue-white flare */
    ParticleBurst flare = {
        .origin    = pos,
        .count     = 8,
        .speed_min = 40.0f,
        .speed_max = 130.0f,
        .life_min  = 0.2f,
        .life_max  = 0.4f,
        .size_min  = 1.5f,
        .size_max  = 3.0f,
        .spread    = 0.7f,
        .direction = exhaust_angle,
        .drag      = 3.0f,
        .gravity_scale = 0.1f,
        .color     = {140, 200, 255, 255},  /* light blue */
        .color_vary = true,
    };
    particle_emit(&flare);
}

void particle_emit_jetpack_boost_trail(Vec2 pos, Vec2 dash_dir) {
    /* Continuous blue flame trail while dashing with boost active */
    float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);

    /* Blue flame sparks */
    ParticleBurst sparks = {
        .origin    = pos,
        .count     = 2,
        .speed_min = 50.0f,
        .speed_max = 140.0f,
        .life_min  = 0.1f,
        .life_max  = 0.25f,
        .size_min  = 1.5f,
        .size_max  = 3.0f,
        .spread    = 0.35f,
        .direction = exhaust_angle,
        .drag      = 3.0f,
        .gravity_scale = 0.05f,
        .color     = {60, 140, 255, 255},   /* bright blue */
        .color_vary = true,
    };
    particle_emit(&sparks);

    /* Blue-white wisps */
    ParticleBurst wisps = {
        .origin    = pos,
        .count     = 1,
        .speed_min = 20.0f,
        .speed_max = 50.0f,
        .life_min  = 0.15f,
        .life_max  = 0.35f,
        .size_min  = 2.0f,
        .size_max  = 3.5f,
        .spread    = 0.5f,
        .direction = exhaust_angle,
        .drag      = 3.5f,
        .gravity_scale = -0.05f,
        .color     = {160, 210, 255, 200},  /* pale blue */
        .color_vary = true,
    };
    particle_emit(&wisps);
}

void particle_emit_jetpack_boost_idle(Vec2 pos, bool facing_left) {
    /* Ambient blue flame simmering from the player's back while boost is
     * active but the player isn't dashing. Exhaust drifts backward and
     * slightly downward — a subtle idle glow effect. */
    float exhaust_angle = facing_left ? 0.0f : (float)M_PI; /* away from facing */

    /* Small blue sparks drifting backward */
    ParticleBurst sparks = {
        .origin    = pos,
        .count     = 1,
        .speed_min = 15.0f,
        .speed_max = 45.0f,
        .life_min  = 0.12f,
        .life_max  = 0.3f,
        .size_min  = 1.0f,
        .size_max  = 2.5f,
        .spread    = 0.8f,
        .direction = exhaust_angle,
        .drag      = 4.0f,
        .gravity_scale = 0.15f,
        .color     = {50, 130, 255, 220},   /* medium blue */
        .color_vary = true,
    };
    particle_emit(&sparks);

    /* Faint blue-white wisps floating up */
    ParticleBurst wisps = {
        .origin    = pos,
        .count     = 1,
        .speed_min = 8.0f,
        .speed_max = 25.0f,
        .life_min  = 0.15f,
        .life_max  = 0.35f,
        .size_min  = 1.5f,
        .size_max  = 2.5f,
        .spread    = 1.0f,
        .direction = exhaust_angle - 0.3f,  /* slightly upward */
        .drag      = 4.5f,
        .gravity_scale = -0.1f,
        .color     = {140, 200, 255, 160},  /* pale blue, translucent */
        .color_vary = true,
    };
    particle_emit(&wisps);
}

void particle_emit_muzzle_flash(Vec2 pos, Vec2 shoot_dir) {
    float angle = atan2f(shoot_dir.y, shoot_dir.x);

    /* Perpendicular angle for the up/down flare lines */
    float perp = angle + (float)M_PI * 0.5f;

    /* Forward beam flash — bright cyan, fast, tight cone */
    ParticleBurst beam = {
        .origin    = pos,
        .count     = 5,
        .speed_min = 120.0f,
        .speed_max = 250.0f,
        .life_min  = 0.03f,
        .life_max  = 0.07f,
        .size_min  = 1.5f,
        .size_max  = 2.5f,
        .spread    = 0.15f,          /* very tight forward */
        .direction = angle,
        .drag      = 6.0f,
        .gravity_scale = 0.0f,
        .color     = {100, 220, 255, 255},  /* cyan */
        .color_vary = true,
    };
    particle_emit(&beam);

    /* Vertical flare — perpendicular lines (up) */
    ParticleBurst flare_up = {
        .origin    = pos,
        .count     = 3,
        .speed_min = 60.0f,
        .speed_max = 140.0f,
        .life_min  = 0.02f,
        .life_max  = 0.06f,
        .size_min  = 1.0f,
        .size_max  = 2.0f,
        .spread    = 0.12f,
        .direction = perp,
        .drag      = 8.0f,
        .gravity_scale = 0.0f,
        .color     = {160, 240, 255, 255},  /* light cyan */
        .color_vary = true,
    };
    particle_emit(&flare_up);

    /* Vertical flare — perpendicular lines (down) */
    ParticleBurst flare_down = flare_up;
    flare_down.direction = perp + (float)M_PI;  /* opposite perpendicular */
    particle_emit(&flare_down);

    /* Bright white core — instant pop at barrel */
    ParticleBurst core = {
        .origin    = pos,
        .count     = 2,
        .speed_min = 5.0f,
        .speed_max = 20.0f,
        .life_min  = 0.03f,
        .life_max  = 0.06f,
        .size_min  = 2.5f,
        .size_max  = 4.0f,
        .spread    = (float)M_PI,    /* all directions, stays near barrel */
        .direction = 0,
        .drag      = 10.0f,
        .gravity_scale = 0.0f,
        .color     = {200, 255, 255, 255},  /* white-cyan glow */
        .color_vary = false,
    };
    particle_emit(&core);
}

void particle_emit_wall_slide_dust(Vec2 pos, int wall_dir) {
    /* Small dust puffs pushed away from the wall */
    float away_angle = (wall_dir < 0) ? 0.0f : (float)M_PI;  /* away from wall */

    ParticleBurst dust = {
        .origin    = pos,
        .count     = 2,
        .speed_min = 15.0f,
        .speed_max = 40.0f,
        .life_min  = 0.1f,
        .life_max  = 0.25f,
        .size_min  = 1.0f,
        .size_max  = 2.0f,
        .spread    = 0.8f,
        .direction = away_angle,
        .drag      = 4.0f,
        .gravity_scale = -0.1f,    /* slight float-up */
        .color     = {180, 170, 150, 200},  /* dusty tan */
        .color_vary = true,
    };
    particle_emit(&dust);
}
