
// Game.h - Core Tetris game logic (board, piece mechanics, scoring events only)
#pragma once
#include <array>
#include <vector>
#include <random>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <SDL3/SDL.h>
#include "../../core/GravityManager.h"

enum PieceType { I, O, T, S, Z, J, L, PIECE_COUNT };
using Shape = std::array<uint16_t, 4>; // four rotation bitmasks

// Game runtime mode
enum class GameMode { Endless, Cooperate, Challenge };

// Special obstacle blocks used by Challenge mode
enum class AsteroidType : uint8_t { Normal = 0, Armored = 1, Falling = 2, Core = 3 };

struct AsteroidCell {
    AsteroidType type{AsteroidType::Normal};
    uint8_t hitsRemaining{1};
    bool gravityEnabled{false};
    uint8_t visualState{0};
};

class Game {
public:
    static constexpr int COLS = 10;
    static constexpr int ROWS = 20;
    static constexpr int TILE = 28; // logical cell size in pixels (render layer decides use)

    struct Piece { PieceType type{PIECE_COUNT}; int rot{0}; int x{3}; int y{-2}; };

    explicit Game(int startLevel = 0, GameMode mode = GameMode::Endless) : mode(mode) { reset(startLevel); }
    void reset(int startLevel = 0);
    void startChallengeRun(int startingLevel = 1); // resets stats and starts challenge level 1 (or provided)
    void beginNextChallengeLevel(); // advances to the next challenge level preserving score/time

    // Simulation -----------------------------------------------------------
    void tickGravity(double frameMs);          // advance gravity accumulator & drop
    void softDropBoost(double frameMs);        // accelerate fall while held
    void hardDrop();                           // instant drop & lock
    void setSoftDropping(bool on);             // mark if player holds Down
    void move(int dx);                         // horizontal move
    void rotate(int dir);                      // +1 cw, -1 ccw (simple wall-kick)
    void holdCurrent();                        // swap with hold (once per spawn)

    // Accessors -----------------------------------------------------------
    const std::array<int, COLS*ROWS>& boardRef() const { return board; }
    const Piece& current() const { return cur; }
    const Piece& next() const { return nextPiece; }
    const Piece& held() const { return hold; }
    bool canHoldPiece() const { return canHold; }
    bool isGameOver() const { return gameOver; }
    bool isPaused() const { return paused; }
    void setPaused(bool p);
    GameMode getMode() const { return mode; }
    void setMode(GameMode m) { mode = m; }
    int score() const { return _score; }
    int lines() const { return _lines; }
    int level() const { return _level; }
    int challengeLevel() const { return challengeLevelIndex; }
    int asteroidsRemaining() const { return asteroidsRemainingCount; }
    int asteroidsTotal() const { return asteroidsTotalThisLevel; }
    bool isChallengeComplete() const { return challengeComplete; }
    bool isChallengeLevelActive() const { return challengeLevelActive; }
    bool isChallengeAdvanceQueued() const { return challengeAdvanceQueued; }
    int queuedChallengeLevel() const { return challengeQueuedLevel; }
    int consumeQueuedChallengeLevel(); // returns next level if queued, else 0
    int startLevelBase() const { return startLevel; }
    double elapsed() const; // Now calculated from start time
    void updateElapsedTime(); // Update elapsed time from system clock
    bool isSoftDropping() const { return softDropping; }
    const std::array<std::optional<AsteroidCell>, COLS*ROWS>& asteroidCells() const { return asteroidGrid; }
    const std::vector<SDL_Point>& getRecentAsteroidExplosions() const { return recentAsteroidExplosions; }
    void clearRecentAsteroidExplosions() { recentAsteroidExplosions.clear(); }
    
    // Block statistics
    const std::array<int, PIECE_COUNT>& getBlockCounts() const { return blockCounts; }
    
    // Line clearing effects support
    bool hasCompletedLines() const { return !completedLines.empty(); }
    const std::vector<int>& getCompletedLines() const { return completedLines; }
    void clearCompletedLines(); // Actually remove the lines from the board
    
    // Sound effect callbacks
    using SoundCallback = std::function<void(int)>; // Callback for line clear sounds (number of lines)
    using LevelUpCallback = std::function<void(int)>; // Callback for level up sounds
    using AsteroidDestroyedCallback = std::function<void(AsteroidType)>; // Callback when an asteroid is fully destroyed
    void setSoundCallback(SoundCallback callback) { soundCallback = callback; }
    void setLevelUpCallback(LevelUpCallback callback) { levelUpCallback = callback; }
    void setAsteroidDestroyedCallback(AsteroidDestroyedCallback callback) { asteroidDestroyedCallback = callback; }

    // Shape helper --------------------------------------------------------
    static bool cellFilled(const Piece& p, int cx, int cy);

    // Gravity tuning accessors (public API for HUD/tuning)
    void setGravityGlobalMultiplier(double m) { gravityGlobalMultiplier = m; }
    double getGravityGlobalMultiplier() const;
    double getGravityMs() const;
    double getFallAccumulator() const { return fallAcc; } // Debug: time accumulated toward next drop
    void setLevelGravityMultiplier(int level, double m);

    // Visual effect hooks
    void updateVisualEffects(double frameMs);
    bool hasHardDropShake() const { return hardDropShakeTimerMs > 0.0; }
    double hardDropShakeStrength() const;
    const std::vector<SDL_Point>& getHardDropCells() const { return hardDropCells; }
    uint32_t getHardDropFxId() const { return hardDropFxId; }
    uint64_t getCurrentPieceSequence() const { return pieceSequence; }
    // Additional stats
    int tetrisesMade() const { return _tetrisesMade; }
    int maxCombo() const { return _maxCombo; }
    int comboCount() const { return _comboCount; }

private:
    static constexpr int ASTEROID_BASE = 100; // sentinel offset for board encoding
    static constexpr int ASTEROID_MAX_LEVEL = 100;

    std::array<int, COLS*ROWS> board{}; // 0 empty else color index
    Piece cur{}, hold{}, nextPiece{};   // current, held & next piece
    bool canHold{true};
    bool paused{false};
    std::vector<PieceType> bag;         // 7-bag randomizer
    std::mt19937 rng{ std::random_device{}() };
    std::array<int, PIECE_COUNT> blockCounts{}; // Count of each piece type used

    int _score{0};
    int _lines{0};
    int _level{1};
    int _tetrisesMade{0};
    int _currentCombo{0};
    int _maxCombo{0};
    int _comboCount{0};
    double gravityMs{800.0};
    double fallAcc{0.0};
    Uint64 _startTime{0}; // Performance counter at game start
    Uint64 _pausedTime{0}; // Time spent paused (in performance counter ticks)
    Uint64 _lastPauseStart{0}; // When the current pause started
    bool gameOver{false};
    int startLevel{0};
    bool softDropping{false}; // true while player holds Down key
    
    // Line clearing support
    std::vector<int> completedLines; // Rows that are complete and ready for effects
    
    // Sound effect callbacks
    SoundCallback soundCallback;
    LevelUpCallback levelUpCallback;
    AsteroidDestroyedCallback asteroidDestroyedCallback;
    // Gravity tuning -----------------------------------------------------
    // Global multiplier applied to all level timings (use to slow/speed whole-game gravity)
    double gravityGlobalMultiplier{1.0};
    // Gravity manager encapsulates frames table, multipliers and conversions
    GravityManager gravityMgr;
    // Backwards-compatible accessors (delegate to gravityMgr)
    double computeGravityMsForLevel(int level) const;

    // Impact FX timers
    double hardDropShakeTimerMs{0.0};
    static constexpr double HARD_DROP_SHAKE_DURATION_MS = 320.0;
    std::vector<SDL_Point> hardDropCells;
    uint32_t hardDropFxId{0};
    uint64_t pieceSequence{0};

    // Challenge mode state -------------------------------------------------
    GameMode mode{GameMode::Endless};
    int challengeLevelIndex{1};
    int asteroidsRemainingCount{0};
    int asteroidsTotalThisLevel{0};
    bool challengeComplete{false};
    std::array<std::optional<AsteroidCell>, COLS*ROWS> asteroidGrid{};
    uint32_t challengeSeedBase{0};
    std::mt19937 challengeRng{ std::random_device{}() };
    bool challengeLevelActive{false};
    bool challengeAdvanceQueued{false};
    int challengeQueuedLevel{0};
    // Asteroid SFX latency mitigation
    std::optional<AsteroidType> pendingAsteroidDestroyType;
    bool asteroidDestroySoundPreplayed{false};

    // Recent asteroid explosion positions (grid coords) for renderer FX
    std::vector<SDL_Point> recentAsteroidExplosions;
    
    // Expose the internal challenge seed base for deterministic FX/RNG coordination
public:
    uint32_t getChallengeSeedBase() const { return challengeSeedBase; }

    // Helpers for board encoding of asteroids
    static bool isAsteroidValue(int boardValue);
    static AsteroidType asteroidTypeFromValue(int boardValue);
    static int asteroidBoardValue(AsteroidType t);

    // Internal helpers ----------------------------------------------------
    void refillBag();
    void spawn();
    bool collides(const Piece& p) const;
    void lockPiece();
    int checkLines(); // Find completed lines and store them
    void actualClearLines(); // Actually remove lines from board
    bool tryMoveDown(); // one-row fall; returns true if moved
    void clearAsteroidGrid();
    void setupChallengeLevel(int level, bool preserveStats);
    void placeAsteroidsForLevel(int level);
    AsteroidType chooseAsteroidTypeForLevel(int level);
    AsteroidCell makeAsteroidForType(AsteroidType t) const;
    void handleAsteroidsOnClearedRows(const std::vector<int>& clearedRows,
                                      std::array<int, COLS*ROWS>& outBoard,
                                      std::array<std::optional<AsteroidCell>, COLS*ROWS>& outAsteroids);
    void applyAsteroidGravity();
    // Gravity tuning helpers (public API declared above)
};
