#include "game/transition.h"
#include "engine/core.h"
#include "engine/audio.h"
#include "engine/particle.h"
#include "config.h"
#include <string.h>
#include <math.h>
#include <SDL2/SDL.h>

/* ═══════════════════════════════════════════════════
 *  Constants
 * ═══════════════════════════════════════════════════ */

/* ── Elevator timing ── */
#define ELEVATOR_CLOSE_DURATION   0.6f   /* doors slide shut           */
#define ELEVATOR_HOLD_DURATION    0.3f   /* closed with rumble         */
#define ELEVATOR_OPEN_DURATION    0.6f   /* doors slide open           */

#define ELEVATOR_OUT_DURATION  (ELEVATOR_CLOSE_DURATION + ELEVATOR_HOLD_DURATION)
#define ELEVATOR_IN_DURATION   (ELEVATOR_HOLD_DURATION + ELEVATOR_OPEN_DURATION)

/* ── Teleporter timing ── */
#define TELEPORT_DISSOLVE_DURATION    0.5f   /* scanline sweep out     */
#define TELEPORT_FLASH_DURATION       0.15f  /* white flash            */
#define TELEPORT_MATERIALIZE_DURATION 0.5f   /* scanline sweep in      */

#define TELEPORT_OUT_DURATION  (TELEPORT_DISSOLVE_DURATION + TELEPORT_FLASH_DURATION)
#define TELEPORT_IN_DURATION   (TELEPORT_FLASH_DURATION + TELEPORT_MATERIALIZE_DURATION)

/* ── Scanline dissolve parameters ── */
#define SCANLINE_HEIGHT  3     /* pixel height per band              */
#define SCANLINE_STAGGER 0.3f  /* time spread between first/last band */

/* ── Elevator colors ── */
#define ELEV_R 40
#define ELEV_G 42
#define ELEV_B 48

/* ── Sound effects ── */
static Sound s_sfx_teleport;
static bool  s_sfx_loaded = false;

static void ensure_sfx(void) {
    if (s_sfx_loaded) return;
    s_sfx_teleport = audio_load_sound("assets/sounds/teleport.wav");
    s_sfx_loaded = true;
}

/* ═══════════════════════════════════════════════════
 *  Outro duration for a given style
 * ═══════════════════════════════════════════════════ */

static float outro_duration(TransitionStyle style) {
    switch (style) {
        case TRANS_ELEVATOR:   return ELEVATOR_OUT_DURATION;
        case TRANS_TELEPORTER: return TELEPORT_OUT_DURATION;
        default:               return 0.0f;
    }
}

static float intro_duration(TransitionStyle style) {
    switch (style) {
        case TRANS_ELEVATOR:   return ELEVATOR_IN_DURATION;
        case TRANS_TELEPORTER: return TELEPORT_IN_DURATION;
        default:               return 0.0f;
    }
}

/* ═══════════════════════════════════════════════════
 *  Public API
 * ═══════════════════════════════════════════════════ */

void transition_start_out(TransitionState *ts, TransitionStyle out_style) {
    ensure_sfx();
    ts->out_style    = out_style;
    ts->in_style     = TRANS_NONE;
    ts->phase        = TRANS_PHASE_OUT;
    ts->timer        = 0.0f;
    ts->phase_dur    = outro_duration(out_style);
    ts->sound_played = false;
}

void transition_set_in_style(TransitionState *ts, TransitionStyle in_style) {
    ts->in_style = in_style;
}

void transition_update(TransitionState *ts, float dt, Camera *cam) {
    if (ts->phase == TRANS_IDLE || ts->phase == TRANS_PHASE_DONE) return;

    ts->timer += dt;

    /* ── Outro phase ── */
    if (ts->phase == TRANS_PHASE_OUT) {
        /* Play sound once at start of teleporter dissolve. */
        if (ts->out_style == TRANS_TELEPORTER && !ts->sound_played) {
            audio_play_sound(s_sfx_teleport, 80);
            ts->sound_played = true;
        }

        /* Elevator rumble during the hold period. */
        if (ts->out_style == TRANS_ELEVATOR && cam) {
            if (ts->timer > ELEVATOR_CLOSE_DURATION) {
                camera_shake(cam, 3.0f, 0.1f);
            }
        }

        if (ts->timer >= ts->phase_dur) {
            ts->phase = TRANS_PHASE_LOAD;
        }
        return;
    }

    /* ── Load phase: caller must call transition_begin_intro() ── */
    if (ts->phase == TRANS_PHASE_LOAD) {
        return;
    }

    /* ── Intro phase ── */
    if (ts->phase == TRANS_PHASE_IN) {
        /* Elevator rumble at the start of intro (before doors open). */
        if (ts->in_style == TRANS_ELEVATOR && cam) {
            if (ts->timer < ELEVATOR_HOLD_DURATION) {
                camera_shake(cam, 2.5f, 0.1f);
            }
        }

        if (ts->timer >= ts->phase_dur) {
            ts->phase = TRANS_PHASE_DONE;
        }
        return;
    }
}

bool transition_needs_load(const TransitionState *ts) {
    return ts->phase == TRANS_PHASE_LOAD;
}

void transition_begin_intro(TransitionState *ts) {
    ts->phase        = TRANS_PHASE_IN;
    ts->timer        = 0.0f;
    ts->phase_dur    = intro_duration(ts->in_style);
    ts->sound_played = false;
}

bool transition_is_done(const TransitionState *ts) {
    return ts->phase == TRANS_PHASE_DONE;
}

void transition_reset(TransitionState *ts) {
    ts->phase     = TRANS_IDLE;
    ts->timer     = 0.0f;
    ts->phase_dur = 0.0f;
}

/* ═══════════════════════════════════════════════════
 *  Elevator rendering helpers
 * ═══════════════════════════════════════════════════ */

/* Returns door coverage 0.0 (fully open) to 1.0 (fully closed). */
static float elevator_coverage(const TransitionState *ts) {
    if (ts->phase == TRANS_PHASE_OUT) {
        /* Closing: 0 → 1 over ELEVATOR_CLOSE_DURATION, then hold at 1. */
        float t = ts->timer / ELEVATOR_CLOSE_DURATION;
        if (t > 1.0f) t = 1.0f;
        /* Ease-in-out for smooth motion. */
        return t * t * (3.0f - 2.0f * t);
    }
    if (ts->phase == TRANS_PHASE_IN) {
        /* Hold closed during ELEVATOR_HOLD_DURATION, then open. */
        float open_t = ts->timer - ELEVATOR_HOLD_DURATION;
        if (open_t <= 0.0f) return 1.0f;
        float t = open_t / ELEVATOR_OPEN_DURATION;
        if (t > 1.0f) t = 1.0f;
        float ease = t * t * (3.0f - 2.0f * t);
        return 1.0f - ease;
    }
    /* PHASE_LOAD: fully closed. */
    return 1.0f;
}

static void render_elevator(const TransitionState *ts) {
    float coverage = elevator_coverage(ts);
    if (coverage <= 0.0f) return;

    int half_h = (int)(coverage * (float)SCREEN_HEIGHT * 0.5f + 0.5f);

    SDL_Renderer *r = g_engine.renderer;
    SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_NONE);
    SDL_SetRenderDrawColor(r, ELEV_R, ELEV_G, ELEV_B, 255);

    /* Top door. */
    SDL_Rect top = {0, 0, SCREEN_WIDTH, half_h};
    SDL_RenderFillRect(r, &top);

    /* Bottom door. */
    SDL_Rect bot = {0, SCREEN_HEIGHT - half_h, SCREEN_WIDTH, half_h};
    SDL_RenderFillRect(r, &bot);

    /* Thin bright seam at the meeting edge (visual detail). */
    if (coverage > 0.7f) {
        int seam_y = half_h - 1;
        SDL_SetRenderDrawColor(r, 100, 110, 130, 255);
        SDL_Rect seam_top = {0, seam_y, SCREEN_WIDTH, 1};
        SDL_RenderFillRect(r, &seam_top);

        int seam_bot_y = SCREEN_HEIGHT - half_h;
        SDL_Rect seam_bot = {0, seam_bot_y, SCREEN_WIDTH, 1};
        SDL_RenderFillRect(r, &seam_bot);
    }
}

/* ═══════════════════════════════════════════════════
 *  Teleporter rendering helpers
 * ═══════════════════════════════════════════════════ */

/* Scanline dissolve progress: each band sweeps across the screen.
 * band_idx: which band (0 = top), total_bands: how many bands,
 * global_progress: 0.0–1.0 across the dissolve duration,
 * reverse: if true, sweep right-to-left / bottom-to-top. */
static float band_progress(int band_idx, int total_bands,
                           float global_progress, bool reverse) {
    float band_offset;
    if (reverse) {
        band_offset = (float)(total_bands - 1 - band_idx) / (float)total_bands
                      * SCANLINE_STAGGER;
    } else {
        band_offset = (float)band_idx / (float)total_bands * SCANLINE_STAGGER;
    }
    float local = (global_progress - band_offset) / (1.0f - SCANLINE_STAGGER);
    if (local < 0.0f) local = 0.0f;
    if (local > 1.0f) local = 1.0f;
    return local;
}

static void render_teleporter_scanlines(float global_progress,
                                        bool reverse) {
    int total_bands = SCREEN_HEIGHT / SCANLINE_HEIGHT;
    SDL_Renderer *r = g_engine.renderer;
    SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_NONE);
    SDL_SetRenderDrawColor(r, 0, 0, 0, 255);

    for (int i = 0; i < total_bands; i++) {
        float bp = band_progress(i, total_bands, global_progress, reverse);
        if (bp <= 0.0f) continue;

        int y = i * SCANLINE_HEIGHT;
        int w = (int)(bp * (float)SCREEN_WIDTH + 0.5f);
        if (w <= 0) continue;
        if (w > SCREEN_WIDTH) w = SCREEN_WIDTH;

        /* Alternate sweep direction per band for visual interest. */
        int x = (i % 2 == 0) ? 0 : (SCREEN_WIDTH - w);

        SDL_Rect band = {x, y, w, SCANLINE_HEIGHT};
        SDL_RenderFillRect(r, &band);
    }
}

static void render_teleporter_flash(float alpha_f) {
    if (alpha_f <= 0.0f) return;
    uint8_t alpha = (uint8_t)(alpha_f * 255.0f);
    if (alpha == 0) return;

    SDL_Renderer *r = g_engine.renderer;
    SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);
    SDL_SetRenderDrawColor(r, 255, 255, 255, alpha);
    SDL_Rect rect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
    SDL_RenderFillRect(r, &rect);
}

static void render_teleporter(const TransitionState *ts) {
    if (ts->phase == TRANS_PHASE_OUT) {
        /* Phase 1: scanline dissolve, then flash builds up. */
        if (ts->timer < TELEPORT_DISSOLVE_DURATION) {
            float progress = ts->timer / TELEPORT_DISSOLVE_DURATION;
            render_teleporter_scanlines(progress, false);
        } else {
            /* Dissolve complete — full black + rising flash. */
            render_teleporter_scanlines(1.0f, false);
            float flash_t = (ts->timer - TELEPORT_DISSOLVE_DURATION)
                            / TELEPORT_FLASH_DURATION;
            if (flash_t > 1.0f) flash_t = 1.0f;
            render_teleporter_flash(flash_t);
        }
        return;
    }

    if (ts->phase == TRANS_PHASE_LOAD) {
        /* Fully covered: black + white flash. */
        render_teleporter_scanlines(1.0f, false);
        render_teleporter_flash(1.0f);
        return;
    }

    if (ts->phase == TRANS_PHASE_IN) {
        /* Phase 2: flash fades out, then scanlines recede. */
        if (ts->timer < TELEPORT_FLASH_DURATION) {
            /* Flash fading over black. */
            render_teleporter_scanlines(1.0f, true);
            float flash_t = 1.0f - ts->timer / TELEPORT_FLASH_DURATION;
            render_teleporter_flash(flash_t);
        } else {
            /* Scanlines receding to reveal the new level. */
            float mat_t = (ts->timer - TELEPORT_FLASH_DURATION)
                          / TELEPORT_MATERIALIZE_DURATION;
            if (mat_t > 1.0f) mat_t = 1.0f;
            /* Progress inverted: 1.0 = fully covered, 0.0 = revealed. */
            render_teleporter_scanlines(1.0f - mat_t, true);
        }
        return;
    }
}

/* ═══════════════════════════════════════════════════
 *  Render dispatch
 * ═══════════════════════════════════════════════════ */

void transition_render(const TransitionState *ts) {
    if (ts->phase == TRANS_IDLE || ts->phase == TRANS_PHASE_DONE) return;

    /* Pick the active style based on which phase we are in. */
    TransitionStyle style = (ts->phase == TRANS_PHASE_IN)
                            ? ts->in_style : ts->out_style;

    switch (style) {
        case TRANS_ELEVATOR:   render_elevator(ts);   break;
        case TRANS_TELEPORTER: render_teleporter(ts); break;
        default: break;
    }
}

/* ═══════════════════════════════════════════════════
 *  Name ↔ enum conversion
 * ═══════════════════════════════════════════════════ */

TransitionStyle transition_style_from_name(const char *name) {
    if (!name) return TRANS_NONE;
    if (strcmp(name, "spacecraft")  == 0) return TRANS_SPACECRAFT;
    if (strcmp(name, "elevator")    == 0) return TRANS_ELEVATOR;
    if (strcmp(name, "teleporter")  == 0) return TRANS_TELEPORTER;
    return TRANS_NONE;
}

const char *transition_style_name(TransitionStyle style) {
    switch (style) {
        case TRANS_SPACECRAFT:  return "spacecraft";
        case TRANS_ELEVATOR:    return "elevator";
        case TRANS_TELEPORTER:  return "teleporter";
        default:                return "none";
    }
}
