#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif

#include <windows.h>
#include <cstdint>
#include <iostream>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <functional>
#include <string>
#include <vector>

#include "memory/imemory_reader.hpp"
#include "memory/offsets_store.hpp"

// ==========================================================
// CẤU TRÚC CAMERA VÀ SƯƠNG MÙ TRONG POE2 v0.5.5
// (Tham chiếu docs/development/02_engine_visual_mods.md
//  và docs/development/28_standalone_visual_tool_zoom_and_fog.md)
// ==========================================================
#pragma pack(push, 1)
struct POE2_CameraInstance {
    uint64_t vtable;               // +0x00
    float    posX, posY, posZ;     // +0x08 (Tọa độ camera)
    float    targetX, targetY, targetZ; // +0x14 (Điểm ngắm - nhân vật)
    float    pitch, yaw, roll;     // +0x20
    float    currentDistance;      // +0x2C (Mặc định: ~38.0f - 45.0f)
    float    minDistance;          // +0x30 (Zoom gần nhất: ~15.0f)
    float    maxDistance;          // +0x34 (Giới hạn zoom xa mặc định: ~45.0f)
    float    fieldOfView;          // +0x38 (FOV dọc ~0.785 rad)
    float    zNear;                // +0x3C (Mặt phẳng cắt gần: ~1.0f)
    float    zFar;                 // +0x40 (Mặt phẳng cắt xa mặc định: ~150.0f)
    float    zoomSmoothingSpeed;   // +0x44
};

struct POE2_FogConstants {
    float fogDensity;              // +0x00 (Mặc định: 1.0f)
    float fogStartDistance;        // +0x04
    float fogEndDistance;          // +0x08
    float heightFalloff;           // +0x0C
};
#pragma pack(pop)

// ==========================================================
// Bản ghi Code Patch trong phân vùng .text (Native Smooth Zoom)
// ==========================================================
struct CodePatchRecord {
    std::string name;
    uintptr_t address = 0;
    std::vector<uint8_t> originalBytes;
    std::vector<uint8_t> patchedBytes;
    bool isPatched = false;

    bool IsValid() const {
        return address != 0 && !originalBytes.empty() && originalBytes.size() == patchedBytes.size();
    }
};

inline constexpr float kVisualZoomMin = 1.0f;
inline constexpr float kVisualZoomMax = 8.0f;
inline constexpr float kVisualZoomStep = 0.10f;
inline constexpr float kVisualZFarCullingScale = 1.35f;

// POE2 Camera VTABLE và Offsets (Bản build POE2 v0.5.5, Base 0x7FF6AFC20000 -> VTABLE 0x7FF6B31C24C0)
inline constexpr uintptr_t kPoe2CameraVtableRva = 0x35A24C0;
inline constexpr uintptr_t kPoe2CameraZoomOffset = 0x520;       // Current Zoom float
inline constexpr uintptr_t kPoe2CameraZoomTargetOffset = 0x528; // Target Zoom float
inline constexpr uintptr_t kPoe2CameraZoomAltOffset = 0x528;    // Alias for backward compatibility
inline constexpr uintptr_t kPoe2CameraDistanceOffset = 0x438;   // 3D Camera Distance float
inline constexpr uintptr_t kPoe2CameraMatrixOffset = 0x1A0;     // WorldToScreen 4x4 float matrix
inline constexpr uintptr_t kPoe2InGameStateStaticRva = 0x4430108; // Static pointer to InGameState
inline constexpr uintptr_t kPoe2CameraInGameStateOffset = 0x368;  // InGameState + 0x368 -> Camera
inline constexpr uintptr_t kPoe2RealZoomClampRva = 0x16D5FC;    // Live minss clamp instruction
inline constexpr const char* kPoe2RealZoomClampPattern = "F3 0F 58 8E 28 05 00 00 F3 0F 5F C8 F3 0F 5D 0D ? ? ? ? F3 0F 11 8E 28 05 00 00";

inline constexpr uintptr_t kPoe2ZoomClampRva1 = 0x28256A9; // minss xmm1, [rip+0DE45F3h]
inline constexpr uintptr_t kPoe2ZoomClampRva2 = 0x2826927; // minss xmm1, [rip+0DE3375h]

// POE2 AreaInstance và Minimap Exploration Offsets (Bản build POE2 v0.5.5)
inline constexpr uintptr_t kPoe2AreaInstanceOffset = 0x290;          // InGameState + 0x290 -> AreaInstance
inline constexpr uintptr_t kPoe2CollisionBufferOffset = 0x9a0;       // std::vector<uint8_t> Collision Grid copy 1
inline constexpr uintptr_t kPoe2TerrainBufferOffset = 0x9b8;         // std::vector<uint8_t> Base Terrain Grid (Collision copy 2)
inline constexpr uintptr_t kPoe2ExplorationBufferOffset = 0x9d0;     // std::vector<uint8_t> REAL Exploration Grid Buffer (Minimap Fog of War)
inline constexpr uintptr_t kPoe2TerrainHeightBufferOffset = 0x9e8;   // std::vector<uint8_t> Terrain Height Grid

// POE2 DirectX Minimap Visibility Shader Code Patches (Build 6AA2213C)
inline constexpr uintptr_t kPoe2MinimapGridFillRva = 0x120D8F8;
inline constexpr const char* kPoe2MinimapGridFillPattern = "48 8B 41 70 45 33 DB 48 39 41 68 75 1F 44 38 59 61 75 1F";

inline constexpr uintptr_t kPoe2MinimapUniformRva = 0x120E480;
inline constexpr const char* kPoe2MinimapUniformPattern = "41 80 7F 61 00 74 05 0F 28 C7 EB 03 0F 28 C8";

inline constexpr uintptr_t kPoe2MinimapBoundRva = 0x120E40F;
inline constexpr const char* kPoe2MinimapBoundPattern = "0F 2F 15 ? ? ? ? 0F 97 C0 89 85 78 0B 00 00";

inline float ClampVisualZoom(float multiplier) {
    return std::clamp(multiplier, kVisualZoomMin, kVisualZoomMax);
}

inline float VisualZFarTarget(float origZFar, float multiplier) {
    return origZFar * (ClampVisualZoom(multiplier) * kVisualZFarCullingScale);
}

inline float VisualMaxDistanceTarget(float origMaxDistance, float multiplier) {
    return origMaxDistance * ClampVisualZoom(multiplier);
}

// ==========================================================
// VisualModsEngine - Can thiệp góc nhìn & loại bỏ sương mù
//
// 1. UnlockZoomCap: chỉ mở trần maxDistance + zFar; con lăn chọn currentDistance.
// 2. ApplyZoom: ghi cứng triad (CLI Core --zoom, tương thích cũ).
// 3. StepZoom: từng nấc con lăn, kẹp [1.0, cap] ⊆ [1.0, 4.0].
// 4. Volumetric Fog: fogDensity = 0.0f.
// 5. 1-Frame Panic Restore.
// ==========================================================
class VisualModsEngine {
public:
    explicit VisualModsEngine(IMemoryReader& reader);
    ~VisualModsEngine();

    void SetCameraAddress(uintptr_t addr);
    uintptr_t CameraAddress() const { return m_cameraAddr; }

    void SetFogAddress(uintptr_t addr);
    uintptr_t FogAddress() const { return m_fogDensityAddr; }

    // Áp dụng Zoom Hack kèm thuật toán bù trừ Culling (zFar fix)
    // multiplier: [1.0f, 4.0f], mặc định 2.2f — ghi cứng currentDistance
    bool ApplyZoom(float zoomMultiplier = 2.2f);

    // Mở trần zoom (maxDistance + zFar). Không ép currentDistance.
    bool UnlockZoomCap(float maxMultiplier = kVisualZoomMax);

    // Đổi trần; nếu current > cap thì hạ current xuống cap.
    bool SetZoomCap(float maxMultiplier);

    // Con lăn: delta > 0 zoom xa, delta < 0 zoom gần (không xuống dưới 1.0x).
    bool StepZoom(float delta);

    // ==========================================================
    // Code Patching trong phân vùng .text (Native 144 FPS, Zero Jitter)
    // ==========================================================
    bool ApplyZoomCodePatch(float maxMultiplier = kVisualZoomMax);
    bool RestoreZoomCodePatch();
    bool IsCodePatchActive() const;
    uintptr_t ZoomClampAddress() const { return m_zoomCodePatch.address; }
    void SetZoomClampAddress(uintptr_t addr);
    void AddZoomClampAddress(uintptr_t addr);
    const CodePatchRecord& ZoomCodePatchRecord() const { return m_zoomCodePatch; }
    const std::vector<CodePatchRecord>& ExtraZoomCodePatches() const { return m_extraZoomCodePatches; }

    // POE2 Native Zoom Clamp & Float Mod
    uintptr_t MaxZoomFloatAddress() const { return m_maxZoomFloatAddr; }
    void SetMaxZoomFloatAddress(uintptr_t addr) { m_maxZoomFloatAddr = addr; }
    float OrigMaxZoomFloat() const { return m_origMaxZoomFloat; }
    void SetOrigMaxZoomFloat(float val) { m_origMaxZoomFloat = val; m_origMaxZoomFloatSaved = true; }
    bool IsMaxZoomFloatPatched() const { return m_maxZoomFloatPatched; }

    // POE2 Native Atlas Fog Patch
    uintptr_t FogCodeAddress() const { return m_fogCodePatch.address; }
    void SetFogCodeAddress(uintptr_t addr) { m_fogCodePatch.address = addr; }
    const CodePatchRecord& FogCodePatchRecord() const { return m_fogCodePatch; }
    bool IsFogCodePatchActive() const { return m_fogCodePatch.isPatched; }

    // Hỗ trợ phương án NOP 8 byte minss (mở vô hạn)
    bool NopZoomClampInstruction(bool enable = true);
    void SetNopClampInstruction(bool enable) { m_nopClampInstruction = enable; }
    bool IsNopClampInstruction() const { return m_nopClampInstruction; }

    // Ghi bộ nhớ với VirtualProtectEx tự động
    bool WriteWithProtect(uintptr_t address, const void* buffer, size_t size);

    // Khôi phục Camera về mặc định ban đầu
    bool RestoreCamera();

    // Vô hiệu hóa sương mù thể tích (Volumetric Fog) và Atlas Fog
    bool DisableFog();

    // Khôi phục sương mù về giá trị ban đầu
    bool RestoreFog();

    // Mở sáng toàn bộ Minimap trong map chiến đấu (In-Map Minimap Reveal qua Exploration Grid)
    bool RevealEntireMinimap();

    // Auto-Reveal Minimap
    bool IsAutoRevealMinimap() const { return m_autoRevealMinimap; }
    void SetAutoRevealMinimap(bool enable) { m_autoRevealMinimap = enable; }
    uintptr_t LastRevealedAreaInstance() const { return m_lastRevealedAreaInstance; }
    void ResetMinimapRevealTracking() {
        m_lastRevealedAreaInstance = 0;
        m_lastRevealCheckTime = {};
    }

    using MinimapRevealCallback = std::function<void()>;
    void SetOnMinimapRevealed(MinimapRevealCallback cb) { m_onMinimapRevealed = std::move(cb); }

    // POE2 DirectX Minimap Visibility Shader Code Patches (Doc 43)
    bool ApplyMinimapShaderPatches();
    bool RestoreMinimapShaderPatches();
    bool AreMinimapShaderPatchesActive() const;
    const CodePatchRecord& MinimapGridPatchRecord() const { return m_minimapGridCodePatch; }
    const CodePatchRecord& MinimapUniformPatchRecord() const { return m_minimapUniformCodePatch; }
    const CodePatchRecord& MinimapBoundPatchRecord() const { return m_minimapBoundCodePatch; }

    // Minimap Zoom: Gửi xung phím Numpad + (VK_ADD) hoặc Numpad - (VK_SUBTRACT) qua SendInput
    bool StepMinimapZoom(int delta);

    // Hoàn nguyên toàn bộ Camera và Fog trong 1 frame (phím Pause/Break, Ctrl+Shift+F12)
    void PanicRestoreAll();

    // Nạp cấu hình địa chỉ / RVA từ OffsetsStore
    void LoadFromOffsetsStore(const OffsetsStore& store, uintptr_t moduleBase = 0, size_t moduleSize = 0);

    // Cung cấp API quản lý phạm vi module
    void SetModuleBounds(uintptr_t base, size_t size) {
        m_moduleBase = base;
        m_moduleSize = size;
        if (base != 0) {
            if (m_minimapGridCodePatch.address == 0) m_minimapGridCodePatch.address = base + kPoe2MinimapGridFillRva;
            if (m_minimapUniformCodePatch.address == 0) m_minimapUniformCodePatch.address = base + kPoe2MinimapUniformRva;
            if (m_minimapBoundCodePatch.address == 0) m_minimapBoundCodePatch.address = base + kPoe2MinimapBoundRva;
        }
    }
    uintptr_t ModuleBase() const { return m_moduleBase; }
    size_t ModuleSize() const { return m_moduleSize; }

    // Tự động duy trì trạng thái Zoom và Xóa Fog (chống game reset sau loading map)
    void Maintain();

    // Tự động dò tìm địa chỉ Camera và Fog trên tiến trình game thật qua AOB / Structural Scan
    bool AutoDetect(uint32_t pid, OffsetsStore* store = nullptr);

    // Trạng thái hiện thời
    bool IsZoomActive() const { return m_zoomActive; }
    float CurrentZoomMultiplier() const { return m_currentZoomMultiplier; }
    float ZoomCap() const { return m_zoomCap; }
    bool IsCapUnlockActive() const { return m_capUnlockOnly; }
    bool IsWheelDriveActive() const { return m_wheelDriveActive; }
    bool IsFogDisabled() const { return m_fogDisabled || m_fogCodePatch.isPatched; }

    float OrigMaxDistance() const { return m_origMaxDistance; }
    float OrigZFar() const { return m_origZFar; }
    float OrigFogDensity() const { return m_origFogDensity; }

    float ReadLiveZoomMultiplier() const;

    // POE2 Camera VTABLE & Type Identification
    uintptr_t CameraVtableAddress() const;
    void SetCameraVtableAddress(uintptr_t addr) { m_cameraVtableOverride = addr; }
    bool IsPoe2Camera() const;
    void SetIsPoe2Camera(bool isPoe2) { m_isPoe2Camera = isPoe2; }
    float OrigPoe2Zoom() const { return m_origPoe2Zoom; }

    // Heap camera bị free / ghi nhầm: false → AutoDetect lại.
    bool CameraLooksValid(uintptr_t moduleBase = 0, size_t moduleSize = 0) const;

private:
    bool EnsureOrigCameraSaved();
    bool WriteMaxAndZFar(float capMultiplier);
    bool WriteCurrentDistance(float multiplier);

    IMemoryReader& m_reader;
    uintptr_t m_cameraAddr = 0;
    uintptr_t m_fogDensityAddr = 0;

    // Phạm vi module chính để kiểm tra tính hợp lệ của vtable
    uintptr_t m_moduleBase = 0;
    size_t m_moduleSize = 0;

    // VTABLE override & POE2 camera tracking
    uintptr_t m_cameraVtableOverride = 0;
    mutable bool m_isPoe2Camera = false;
    float m_origPoe2Zoom = 1.0f;

    // Quản lý Code Patching trong .text (Zoom & Fog)
    CodePatchRecord m_zoomCodePatch;
    std::vector<CodePatchRecord> m_extraZoomCodePatches;
    CodePatchRecord m_fogCodePatch;

    // Quản lý Code Patching trong .text (Minimap Shader Patches - Doc 43)
    CodePatchRecord m_minimapGridCodePatch;
    CodePatchRecord m_minimapUniformCodePatch;
    CodePatchRecord m_minimapBoundCodePatch;

    uintptr_t m_maxZoomFloatAddr = 0;
    float m_origMaxZoomFloat = 2.0f;
    bool m_origMaxZoomFloatSaved = false;
    bool m_maxZoomFloatPatched = false;
    bool m_nopClampInstruction = false;

    // Lưu trữ giá trị mặc định gốc của game để khôi phục sạch
    float m_origMaxDistance = 45.0f;
    float m_origZFar = 150.0f;
    float m_origFogDensity = 1.0f;

    bool m_origCameraSaved = false;
    bool m_origFogSaved = false;

    // Auto-Reveal Minimap
    bool m_autoRevealMinimap = true;
    uintptr_t m_lastRevealedAreaInstance = 0;
    std::chrono::steady_clock::time_point m_lastRevealCheckTime{};
    MinimapRevealCallback m_onMinimapRevealed;

    bool m_zoomActive = false;
    bool m_fogDisabled = false;
    bool m_capUnlockOnly = false;
    bool m_wheelDriveActive = false;
    float m_currentZoomMultiplier = 1.0f;
    float m_zoomCap = kVisualZoomMin;
};

inline bool VisualModsEngine::IsCodePatchActive() const {
    if (m_zoomCodePatch.isPatched) return true;
    for (const auto& rec : m_extraZoomCodePatches) {
        if (rec.isPatched) return true;
    }
    return m_maxZoomFloatPatched;
}

inline void VisualModsEngine::SetZoomClampAddress(uintptr_t addr) {
    m_zoomCodePatch.address = addr;
    m_extraZoomCodePatches.clear();
}

