#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>

#include "common/protocol.hpp"
#include "memory/imemory_reader.hpp"
#include "memory/game_layout.hpp"

// ==========================================================
// EntityManager - Bóc tách trạng thái game qua chuỗi con trỏ
// (Giai đoạn 2 - Nhiệm vụ 2.3 theo WBS)
//
// Đường đọc (giống hệt reverse engineering game thật):
//   [module .data static] -> InGameState
//   InGameState + 0x3E8   -> EntityMap { capacity, count, entries[] }
//   InGameState + 0x10    -> PlayerState
// Backend-agnostic: chạy trên Simulated / RPM / DMA / Kernel.
// ==========================================================

struct EntityManagerConfig {
    float maxEntityDistance = 150.0f;  // lọc entity trong tầm 150 đơn vị (theo tài liệu)
    size_t maxEntities = MAX_ENTITIES_PER_SNAPSHOT; // 256
};

class EntityManager {
public:
    explicit EntityManager(IMemoryReader& reader,
                           const EntityManagerConfig& config = {});

    // Đọc 1 lượt toàn bộ trạng thái. Trả về false nếu chuỗi con trỏ hỏng
    // (game chưa load, magic sai, đọc bộ nhớ thất bại...).
    bool Update();

    // Kết quả lần Update() gần nhất
    const game_layout::PlayerStateMemory& Player() const { return m_player; }
    const std::vector<EntityTelemetryData>& Entities() const { return m_entities; }
    size_t TotalEntitiesInMap() const { return m_totalInMap; }
    size_t TotalEntitiesFiltered() const { return m_totalFiltered; }

    uint32_t AreaId() const { return m_areaId; }
    uint32_t AreaSeed() const { return m_areaSeed; }
    uint32_t GameTick() const { return m_gameTick; }

    // Cấu hình động cho game thật
    void SetInGameAddress(uintptr_t addr) { m_inGameAddress = addr; }
    uintptr_t InGameAddress() const { return m_inGameAddress; }
    void SetEntityMapOffset(uintptr_t off) { m_entityMapOffset = off; }
    uintptr_t EntityMapOffset() const { return m_entityMapOffset; }
    void SetSimulated(bool sim) { m_isSimulated = sim; }
    void SetMainBase(uintptr_t base) { m_mainBase = base; }
    uintptr_t MainBase() const { return m_mainBase; }
    void SetPlayerAddress(uintptr_t addr) { m_playerAddr = addr; }
    uintptr_t PlayerAddress() const { return m_playerAddr; }
    void SetPlayerXyzAddress(uintptr_t addr) { m_xyzAddr = addr; }
    uintptr_t PlayerXyzAddress() const { return m_xyzAddr; }
    void SetPlayerPosition(float x, float y, float z) { m_playerX = x; m_playerY = y; m_playerZ = z; }
    void SetEntityArraySlot(uintptr_t slot) { m_entityArraySlot = slot; }
    uintptr_t EntityArraySlot() const { return m_entityArraySlot; }

    // Điền dữ liệu đã đọc vào packet telemetry
    void FillTelemetry(TelemetryPacket& pkt) const;

    // Xóa sạch danh sách quái và reset slot mảng khi chuyển cảnh / hồi sinh
    void ClearEntities() {
        m_entities.clear();
        m_entPtrMap.clear();
        m_totalInMap = 0;
        m_entityArraySlot = 0;
        m_cachedCompOff = 0;
        m_cachedPosOff = 0;
    }

private:
    bool ReadInGameState(uintptr_t& outInGame);
    bool ReadPlayer(uintptr_t inGame);
    bool ReadEntities(uintptr_t inGame);
    bool ScanProximityMonsters();

    IMemoryReader& m_reader;
    EntityManagerConfig m_config;

    uintptr_t m_inGameAddress = 0;
    uintptr_t m_entityMapOffset = 0;
    uintptr_t m_mainBase = 0;
    uintptr_t m_playerAddr = 0;
    uintptr_t m_xyzAddr = 0;
    uintptr_t m_entityArraySlot = 0;
    uintptr_t m_entityStatOffset = 0x3B8;
    uintptr_t m_cachedCompOff = 0;
    uintptr_t m_cachedPosOff = 0;
    bool m_isSimulated = false;
    float m_playerX = 0.0f;
    float m_playerY = 0.0f;
    float m_playerZ = 0.0f;
    uint64_t m_lastProximityScanMs = 0;

    std::unordered_map<uint32_t, uintptr_t> m_entPtrMap;

    game_layout::PlayerStateMemory m_player{};
    std::vector<EntityTelemetryData> m_entities;
    size_t m_totalInMap = 0;
    size_t m_totalFiltered = 0;
    uint32_t m_areaId = 0;
    uint32_t m_areaSeed = 0;
    uint32_t m_gameTick = 0;
};