#pragma once

#if defined( __EMSCRIPTEN__ ) || defined( __ANDROID__ )
#include <GLES3/gl3.h>
#else
#include <glad/glad.h>
#endif

#include <entt/entt.hpp>
#include <SDL3/SDL_events.h>

#include "crArray.h"
#include "crEcsComponents.h"
#include "crRandom.h"

class crApp;

enum class EGameState : uint8_t
{
    TITLE = 0,
    PLAYING,
    PAUSED,    // sim paused — resume or quit
    LEVELUP,   // sim paused — pick one of three upgrades
    RESULT,
};

enum class EUpgrade : uint8_t
{
    RAPID = 0,    // fire faster
    MULTISHOT,    // +1 bullet per volley, fanned
    PIERCE,       // bullets pass through +1 enemy
    DAMAGE,       // +1 damage per bullet
    NOVA,         // shockwave: shorter period, wider
    ENGINE,       // thrust + max speed
    VITALITY,     // +max hp, heal
    MAGNET,       // gem pull radius
    ORBITAL,      // blades circling the ship, damaging on touch
    SEEKER,       // periodic homing missiles with an AoE burst
    CARRIER,      // orbiting pods that launch small self-destructing drones

    _SIZE
};

// level-up choice tier — rolled per offer; higher tiers carry bigger magnitudes and their own color
enum class ERarity : uint8_t
{
    COMMON = 0,
    RARE,
    EPIC,

    _SIZE
};

// a telegraphed enemy spawn — the marker warns, the enemy arrives at spawnStep
struct PendingSpawn
{
    float3       pos       = {};
    uint64_t     spawnStep = 0;
    entt::entity marker    = entt::null;
};

// a telegraphed dart launch — the beam draws the flight line, the dart follows it
struct PendingDart
{
    float3       from      = {};
    float3       dir       = {};
    uint64_t     spawnStep = 0;
    entt::entity marker    = entt::null;
};

// a drifting nebula whirl — visual fill and gravity well in one object
struct Vortex
{
    entt::entity blob   = entt::null;   // additive nebula body (carries the churn emitter)
    entt::entity swirl  = entt::null;   // banded ring, spun render-visibly by the sim
    float3       pos    = {};
    float2       drift  = {};           // slow wander velocity
    float        spin   = 0.0f;         // accumulated swirl angle
};

// a falling meteor — marker on the floor, rock descending from above, AoE on impact
struct PendingMeteor
{
    float3       pos        = {};
    uint64_t     impactStep = 0;
    entt::entity marker     = entt::null;
    entt::entity rock       = entt::null;
};

// behavior class of an enemy — steering, rewards, and indicators branch on this
enum class EEnemyKind : uint8_t
{
    GRUNT = 0,
    RUNNER,
    BRUTE,
    ELITE,
    GUNNER,   // keeps its distance, fires slow dodgeable shots
    DART,     // flies a straight line through the arena, detonates at the boundary
    BOSS,     // giant two-phase hunter — steered by UpdateBoss, not the swarm loop
    SHELL,    // slow high-hp blocker — a moving pillar; auto-fire only targets it as a last resort
};

// tag — a swarm enemy: remaining hits, its type's speed factor, and hit-flash state
struct CEnemy
{
    int32_t    hp       = 1;
    float      speedMul = 1.0f;
    float      flash    = 0.0f;   // 1 on hit, decays — blends the primitive toward white
    color4     baseColor;
    float      baseEmissive = 0.0f;   // the flash rides on top of it, so the resting value has to be remembered
    float3     baseScale = {};        // authored size — the render-clock visual pass modulates around it
    float      phase     = 0.0f;      // personal offset for throb and weave, so the swarm never pulses in unison
    uint64_t   spawnStep = 0;         // birth stamp — drives the spawn pop
    uint64_t   nextLungeStep = 0;   // lunge cooldown; GUNNER reuses it as its fire cooldown
    EEnemyKind kind = EEnemyKind::GRUNT;
    float3     dartDir = {};        // DART only — fixed flight direction
};

// tag — a corpse: no physics, no steering, just a shape blowing out and fading on the sim clock
struct CHusk
{
    float3   baseScale = {};
    color4   baseColor;
    uint64_t bornStep  = 0;
    uint64_t endStep   = 0;
};

// tag — a bullet: when it expires and how many enemies it may still pass through
struct CBullet
{
    uint64_t expireStep;
    int32_t  pierceLeft;
};

// tag — a hostile projectile: straight flight, manual integration, no physics body
struct CEnemyShot
{
    float3   vel        = {};
    uint64_t expireStep = 0;
};

// tag — a homing missile: steered toward its target each step, bursts on arrival or expiry.
// flight/blast params live per missile, so seekers and carrier drones share one update path
struct CMissile
{
    entt::entity target = entt::null;
    uint64_t     expireStep = 0;
    float3       vel    = {};
    float        speed  = 16.0f;
    float        aoe    = 2.4f;
    float        kick   = 7.0f;
    int32_t      damage = 3;
};

// tag — an xp gem: value plus a personal phase for its spin/pulse animation
struct CGem
{
    int32_t value;
    float   phase;
};

// per-run stats — seeded from Tuning at session start, grown by level-ups
struct RunStats
{
    float   fireInterval = 0.0f;
    int32_t bulletCount  = 1;
    int32_t pierce       = 0;
    int32_t damage       = 1;
    float   novaPeriod   = 0.0f;
    float   novaRadius   = 0.0f;
    float   novaKick     = 0.0f;
    float   thrust       = 0.0f;
    float   maxSpeed     = 0.0f;
    float   hpMax        = 0.0f;
    float   magnetRadius = 0.0f;
    int32_t orbCount      = 0;      // ORBITAL weapon — 0 = not unlocked
    float   orbRadius     = 0.0f;
    int32_t missileCount  = 0;      // SEEKER weapon — 0 = not unlocked
    float   missilePeriod = 0.0f;
    int32_t podCount      = 0;      // CARRIER weapon — 0 = not unlocked
    float   dronePeriod   = 0.0f;
};

// live-tunable base knobs — edited from the dev panel (RenderDevUi); RunStats grows from these
struct Tuning
{
    float   thrust       = 150.0f;
    float   shipMass     = 1.0f;
    float   damping      = 2.5f;
    float   maxSpeed     = 14.0f;

    float   fireInterval = 0.18f;
    float   bulletSpeed  = 30.0f;
    float   fireRange    = 18.0f;

    float   enemySpeed   = 4.6f;
    float   enemySteer   = 0.12f;
    int32_t enemyCap     = 1000;
    float   spawnStart   = 1.9f;   // trickle is secondary — formations (PATTERN_PERIOD) drive the pressure
    float   spawnMin     = 0.55f;
    float   spawnFalloff = 0.985f;
    float   speedRamp    = 0.005f;   // enemy speed multiplier growth per second

    float   hpMax        = 100.0f;
    float   contactHit   = 25.0f;    // per-touch chunk damage (i-frames gate the rest)
    float   contactKick  = 11.0f;    // shove the ship gets off the mob when hit

    float   novaPeriod   = 5.0f;
    float   novaRadius   = 6.0f;
    float   novaKick     = 9.0f;

    float   magnetRadius = 3.0f;
    float   gemPullSpeed = 14.0f;

    float   orbRadius     = 2.0f;
    float   orbSpin       = 2.4f;    // rad/s
    int32_t orbDamage     = 2;
    float   missilePeriod = 3.4f;
    float   missileSpeed  = 16.0f;
    int32_t missileDamage = 3;
    float   missileAoE    = 2.4f;
    float   missileKick   = 7.0f;
    float   dronePeriod   = 4.0f;    // per-pod launch cadence (splits across pods)
    float   droneSpeed    = 9.0f;
    int32_t droneDamage   = 2;
    float   droneAoE      = 1.8f;
    float   droneKick     = 5.0f;

    float   camDistance  = 26.0f;
    float   camLead      = 0.3f;
    float   camSpeedZoom = 0.12f;
};

// the game module — the framework's single entry point into src/game/ (bound through Game.h).
//
// NovaSurvivors — survivors-lite mass combat: a zero-g arena, hundreds of physical enemies
// converging on the ship, auto-fire, a periodic shockwave, xp gems, and pick-one-of-three
// level-ups. every enemy is a real rigid body — the pile-ups and the wedge the nova carves
// ARE the game. survival score attack
class Game
{
private:
    // collision categories — bullets ignore each other and the player; everything meets the walls
    static constexpr uint64_t CAT_WORLD  = 1;
    static constexpr uint64_t CAT_PLAYER = 2;
    static constexpr uint64_t CAT_ENEMY  = 4;
    static constexpr uint64_t CAT_BULLET = 8;

    static constexpr int32_t GEM_CAP        = 400;
    static constexpr int32_t ORB_CAP        = 8;
    static constexpr int32_t POD_CAP        = 4;
    static constexpr int32_t VORTEX_CAP     = 2;
    static constexpr int32_t STORM_BLOBS    = 9;

    crApp* _app = nullptr;

    EGameState _state          = EGameState::TITLE;
    EGameState _requestedState = EGameState::TITLE;   // latched — applied between sim steps (Update)

    Tuning   _tune;
    RunStats _stats;
    crRandom _rng;   // session stream — consumed only inside fixed steps

    entt::entity _player      = entt::null;
    float        _shipHeading = 0.0f;   // rad — smoothed toward the thrust direction, cosmetic
    int32_t      _novaColorIndex = 0;
    float        _hp          = 0.0f;
    uint64_t     _invulnUntilStep = 0;    // i-frames after a hit — damage sources check this
    float        _spawnInterval = 1.2f;
    uint64_t     _nextSpawnStep = 0;
    uint64_t     _nextFireStep  = 0;
    uint64_t     _nextNovaStep  = 0;
    uint64_t     _nextPatternStep = 0;    // escalation: periodic formation spawn event
    uint64_t     _nextFrenzyStep  = 0;    // escalation: periodic swarm-wide speed surge
    uint64_t     _frenzyEndStep   = 0;    // frenzy active while step < this
    uint64_t     _nextMissileStep = 0;
    uint64_t     _nextOrbHitStep  = 0;    // orb contact damage tick

    // storm front — a nebula wall sweeping across the arena; hits the ship, herds enemies with it
    bool         _stormActive  = false;
    uint64_t     _nextStormStep = 0;
    uint64_t     _stormEndStep  = 0;      // sweep completes at this step
    float2       _stormNormal   = {};     // travel direction (unit)
    float        _stormOffset   = 0.0f;   // signed distance of the front along the normal
    float        _stormGap      = 0.0f;   // tangent-axis center of the safe gap through the wall
    entt::entity _stormBlobs[ STORM_BLOBS ] = {};   // pooled front visuals, hidden at scale 0

    crArray<entt::entity> _enemies;
    crArray<entt::entity> _bullets;
    crArray<entt::entity> _gems;
    crArray<entt::entity> _missiles;

    entt::entity _orbs[ ORB_CAP ] = {};   // set to entt::null in Init/ExitSession
    int32_t      _orbCount = 0;
    float        _orbAngle = 0.0f;        // accumulated orbit phase — wraps, advanced per fixed step

    entt::entity _pods[ POD_CAP ] = {};   // CARRIER pods — set to entt::null in Init/ExitSession
    int32_t      _podCount      = 0;
    float        _podAngle      = 0.0f;   // counter-rotates against the blades
    uint64_t     _nextDroneStep = 0;
    int32_t      _nextDronePod  = 0;      // round-robin launch pod

    entt::entity _boss           = entt::null;
    entt::entity _bossMarker     = entt::null;
    uint64_t     _bossArriveStep = 0;     // nonzero while the arrival is telegraphed
    uint64_t     _nextBossStep   = 0;
    uint64_t     _nextBossVolleyStep = 0;
    int32_t      _bossHpMax      = 1;
    bool         _bossPhase2     = false;
    float        _sloMo          = 0.0f;  // boss-kill time dilation — decays on the render clock

    int32_t  _kills     = 0;
    int32_t  _score     = 0;
    int32_t  _bestScore = 0;

    int32_t  _xp     = 0;
    int32_t  _xpNeed = 5;
    int32_t  _level  = 1;
    EUpgrade _choices[ 3 ]      = {};
    ERarity  _choiceRarity[ 3 ] = {};

    entt::entity _novaRing = entt::null;   // expanding shell, animated by the sim
    float        _novaRingAge = 99.0f;
    entt::entity _walls[ 6 ] = {};                    // set to entt::null in Init — pulsed render-side

    float                 _latticeFrenzy = 0.0f;      // eased 0..1 surge weight for the floor
    crArray<entt::entity> _lattice;                    // floor honeycomb — static, brightness driven render-side
    crArray<entt::entity> _husks;                     // corpses mid-blowout — drained on the sim clock

    crArray<PendingSpawn> _pendingSpawns;             // telegraphed enemy arrivals
    crArray<PendingDart>  _pendingDarts;              // telegraphed dart launches
    crArray<entt::entity> _enemyShots;                // hostile projectiles in flight
    crArray<PendingMeteor> _meteors;                  // falling rocks awaiting impact
    uint64_t              _nextMeteorStep = 0;

    Vortex  _vortices[ VORTEX_CAP ] = {};
    int32_t _vortexCount = 0;

    // render-side camera state — smoothed follow + damage shake
    float3   _camPos      = {};
    bool     _camValid    = false;
    float    _shakeMag    = 0.0f;
    float    _shakePhase  = 0.0f;
    float    _chromaPulse = 0.0f;   // chromatic-aberration kick — spikes on impacts, decays render-side
    float    _glitchPulse = 0.0f;   // PP tear noise — spikes when the PLAYER takes damage, decays render-side
    float    _hurtFlash   = 0.0f;   // full-screen white flash on a hit — decays render-side
    float    _radialPulse = 0.0f;   // radial blur burst — spikes on shockwaves, decays render-side
    float    _renderDt    = 0.0f;
    float    _fxTime      = 0.0f;

    GLuint  _fxProgram  = 0;    // primitive_nova_survivors shader (flow/warp/bands/ringBand) — game-owned, deleted in Cleanup
    uint8_t _fxMaterial = 0;    // its crBatchedPrimitives material id; set on CPrimitive.material for effect primitives

    int32_t _uiButtonSprite = -1;
    int32_t _uiDimSprite    = -1;
    int32_t _fxSprite       = -1;   // circle32 — soft round
    int32_t _fxSquare       = -1;   // sq16 — hard shard
    int32_t _fxRing         = -1;   // ring64 — expanding hoop

    int32_t _sfxTest = -1;

    int32_t _font16 = -1;   // Orbitron-Medium 16 — main UI text
    int32_t _font12 = -1;   // Orbitron-Medium 12 — small labels

public:
    void Init( crApp* app );
    void Cleanup();

    void Update( crApp* app );                  // render frame, after the sim steps — state transitions apply here
    void FixedUpdatePre( const crApp* app );    // sim step — steering, fire, spawn, nova, gems
    void FixedUpdatePost( const crApp* app );   // sim step — bullet hits, contact damage, expiry, level check
    void HandleEvent( const crApp* app, const SDL_Event* event );
    void Render( const crApp* app );
    void RenderUi( crApp* app );
    void RenderDevUi( const crApp* app );

private:
    void RequestState( EGameState state );
    void EnterSession( crApp* app );
    void ExitSession( crApp* app );

    static void CmdTitle( void* context, int32_t argc, const char** argv );
    static void CmdPlay( void* context, int32_t argc, const char** argv );

    void SpawnArena( const crApp* app );       // hex walls + starfield + fx entities
    void SpawnPlayer( const crApp* app );
    void QueueEnemySpawn( const crApp* app );                     // pick a random ring spot, then QueueEnemySpawnAt
    void QueueEnemySpawnAt( const crApp* app, float3 pos );       // warning marker + scheduled arrival at an explicit spot
    void SpawnEnemy( const crApp* app, float3 pos );              // the actual arrival — type roll happens here
    void SpawnDart( const crApp* app, float3 pos, float3 dir );   // forced DART kind — launched off a drawn flight line
    void SpawnBoss( const crApp* app, float3 pos );
    void SpawnVortex( const crApp* app, float3 pos );
    void SpawnGem( const crApp* app, float3 pos, int32_t value );
    void SpawnOrb( const crApp* app );
    void SpawnPod( const crApp* app );
    void FireBullet( const crApp* app, float3 from, float3 dir, color4 color );
    void FireMissiles( const crApp* app, float3 from );
    void FireDrone( const crApp* app, float3 from );
    void FireEnemyShot( const crApp* app, float3 from, float3 dir );

    void UpdatePlayerControl( const crApp* app );
    void UpdateEnemySteering( const crApp* app );
    void UpdateAutoFire( const crApp* app );      // volley: bulletCount shots fanned around the target
    void UpdateOrbs( const crApp* app );          // orbit placement + contact damage ticks
    void UpdatePods( const crApp* app );          // pod orbit + round-robin drone launches
    void UpdateMissiles( const crApp* app );      // launch, steer, burst — seekers and drones alike
    void UpdateEnemyShots( const crApp* app );    // hostile projectiles: integrate, hit the player, expire
    void UpdateBoss( const crApp* app );          // arrival telegraph + chase / volleys / phase 2
    void UpdateVortices( const crApp* app );      // drift + swirl + pull on enemies, gems, and the ship
    void UpdateMeteors( const crApp* app );       // schedule strikes, animate the fall, detonate
    void UpdateStorm( const crApp* app );         // sweeping nebula front — schedule, advance, damage
    void UpdateSpawner( const crApp* app );
    void UpdateNova( const crApp* app );
    void UpdateNovaRing( const crApp* app );      // sim-side shell expansion
    void UpdateGems( const crApp* app );          // magnet pull + collect -> xp
    void UpdateBulletHits( const crApp* app );
    void UpdateContactDamage( const crApp* app );
    void UpdateExpiry( const crApp* app );
    void CheckLevelUp( const crApp* app );
    void KillPlayer( const crApp* app );

    void DamagePlayer( const crApp* app, float amount, float3 knockDir );   // chunk damage: i-frame gated, knockback, feedback, death check
    bool DamageEnemy( const crApp* app, entt::entity e, int32_t damage );   // flash + spark; true = hp depleted
    void DestroyEnemy( const crApp* app, entt::entity e );                  // score, gems, burst, body + entity teardown

    void AddRipple( float3 pos, float radius, float life, float strength );                            // forwards to crPostProcess::QueueRipple
    void SpawnTimedFx( const crApp* app, const CParticleEmitter& params, float3 pos, float seconds );  // fire-and-forget eruption (CParticleEmitter.killStep)

    void RollChoices();                           // three distinct upgrades + a rarity each for the pause screen
    void ApplyUpgrade( const crApp* app, EUpgrade upgrade, ERarity rarity );

    void UpdateEnemyVisuals( const crApp* app );  // render-side throb / stretch / hit pop / spawn pop — CPrimitive only
    void UpdateLattice( const crApp* app );       // render-side floor honeycomb — lights up around the ship
    void UpdateWallPulse( const crApp* app );     // render-side breathing glow on the arena walls
    void UpdateCamera( crApp* app );
};
