#pragma once
// ── CS2 External C++ WebRadar — GameData ─────────────────────────────────────
// Main frame loop: reads CS2 memory and tracks all game state.
// Matches C# GameData.cs:
//   - Every frame: update local player, check bomb, round info
//   - Every 4th frame: full entity scan
//   - Every 250 frames: map detection
// Fires FrameProduced callback which WebRadarServer::Broadcast subscribes to.

#include "game/threaded_service.h"
#include "game/game_process.h"
#include "game/entity_base.h"
#include "game/entity.h"
#include "game/player.h"

#include <functional>
#include <vector>
#include <memory>
#include <string>
#include <unordered_map>

// ── Grenade info ─────────────────────────────────────────────────────────────
struct GrenadeInfo {
    std::string type;        // "smoke", "he", "flash", "decoy", "molotov", "inferno", "fire"
    float x = 0, y = 0, z = 0;
    float vx = 0, vy = 0;   // velocity for trajectory
    bool did_smoke_effect = false;
    bool is_active = false;
    float smoke_time_left = 0;
    float fire_time_left = 0;
    int fire_count = 0;
    std::vector<std::pair<float, float>> fire_positions;
    bool has_trajectory = false;
    float land_x = 0, land_y = 0;
    std::string trajectory_points;
};

// ── Dropped weapon info ─────────────────────────────────────────────────────
struct DroppedWeapon {
    std::string type;
    std::string class_name;
    float x = 0, y = 0;
};

// ── GameData ─────────────────────────────────────────────────────────────────
class GameData : public ThreadedServiceBase {
public:
    explicit GameData(GameProcess* game_process);
    ~GameData() override;

    // ── Callback ──────────────────────────────────────────────────────────
    // Fired after each successful frame. WebRadarServer subscribes here.
    std::function<void(GameData&)> frame_produced;

    // ── Public state (read by WebRadarServer::Broadcast) ──────────────────
    Player* player() const { return m_player.get(); }
    const std::vector<std::unique_ptr<Entity>>& entities() const { return m_entities; }

    // Bomb state
    bool bomb_planted() const { return m_bomb_planted; }
    long long bomb_plant_time() const { return m_bomb_plant_time; }
    float bomb_x() const { return m_bomb_x; }
    float bomb_y() const { return m_bomb_y; }
    float bomb_z() const { return m_bomb_z; }
    const std::string& bomb_state() const { return m_bomb_state; }
    bool bomb_visible() const { return m_bomb_visible; }
    bool bomb_being_defused() const { return m_bomb_being_defused; }
    float bomb_defuse_count_down() const { return m_bomb_defuse_count_down; }
    int bomb_site_index() const { return m_bomb_site_index; }

    // Round info
    float round_time_remaining() const { return m_round_time_remaining; }
    int round_number() const { return m_round_number; }
    int game_phase() const { return m_game_phase; }
    int t_score() const { return m_t_score; }
    int ct_score() const { return m_ct_score; }

    // Map
    const std::string& current_map_name() const { return m_current_map_name; }

    // Grenades / Dropped weapons
    const std::vector<GrenadeInfo>& grenades() const { return m_grenades; }
    const std::vector<DroppedWeapon>& dropped_weapons() const { return m_dropped_weapons; }

    // Process ref (for observer target writes)
    GameProcess* game_process() const { return m_game_process; }

    // Tick count for internal timing
    long long app_start_tick() const { return m_app_start_tick; }

protected:
    void frame_action() override;

private:
    GameProcess* m_game_process = nullptr;

    // Core objects
    std::unique_ptr<Player> m_player;
    std::vector<std::unique_ptr<Entity>> m_entities;
    int m_max_entities = 64;

    // Frame counters
    int m_frame_count = 0;
    long long m_app_start_tick;

    // Bomb
    bool m_bomb_planted = false;
    long long m_bomb_plant_time = 0;
    float m_bomb_x = 0, m_bomb_y = 0, m_bomb_z = 0;
    std::string m_bomb_state = "carried";
    bool m_bomb_visible = false;
    bool m_bomb_being_defused = false;
    float m_bomb_defuse_count_down = 0;
    int m_bomb_site_index = -1;
    int m_bomb_carrier_pawn_entry_idx = -1;
    std::uintptr_t m_bomb_c4_entity = 0;
    float m_current_game_time = 0;

    // Round info
    float m_round_time_remaining = 115;
    int m_round_number = 0;
    int m_game_phase = 0;
    int m_t_score = 0;
    int m_ct_score = 0;

    // Map
    std::string m_current_map_name = "de_mirage";
    int m_last_round_number_for_map_detection = -1;
    int m_global_vars_map_name_offset = 188; // 0x188

    // Grenades
    std::vector<GrenadeInfo> m_grenades;
    std::vector<GrenadeInfo> m_grenade_buffer;

    // Dropped weapons
    std::vector<DroppedWeapon> m_dropped_weapons;
    std::vector<DroppedWeapon> m_dropped_weapon_buffer;

    // Per-entity effect start tracking
    std::unordered_map<std::uintptr_t, long long> m_deployed_effect_start_ticks;

    // ── Internal frame actions ────────────────────────────────────────────
    void update_player();
    void update_entities();
    void update_bomb();
    void update_round_info();
    void update_map();
    void update_grenades();
    void update_dropped_weapons();
    void update_team_scores();

    // Helpers
    std::uintptr_t read_entity_list_base();
    std::string read_class_name(std::uintptr_t entity_addr);
    int read_highest_entity_index();

    // Constants
    static constexpr int ENTITY_SCAN_INTERVAL = 4;  // every 4th frame
    static constexpr int MAP_DETECT_INTERVAL = 250;  // every 250 frames
};
