#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <cstdint>
#include <vector>
#include <cmath>
#include <algorithm>

#include "memory/terrain_reader.hpp"

namespace navigation {

struct GridCoord {
    int32_t x = 0;
    int32_t y = 0;

    bool operator==(const GridCoord& other) const {
        return x == other.x && y == other.y;
    }
};

enum CellType : uint8_t {
    CELL_WALKABLE = 0,
    CELL_BLOCKED  = 1,
    CELL_HAZARD   = 2
};

class TerrainGrid {
public:
    static constexpr int32_t kGridDim = 256;      // 256 x 256 ô lưới
    static constexpr float kDefaultCellSize = 6.0f; // 6.0 đơn vị thế giới mỗi ô (~1500 units tầm phủ)

    explicit TerrainGrid(float cellSize = kDefaultCellSize);

    void Initialize(float centerX, float centerY);
    void Reset();
    void RecenterIfNeeded(float playerX, float playerY);

    // Nạp dữ liệu bản đồ toàn cảnh gốc đọc từ RAM
    bool LoadFromNativeTerrain(const memory::NativeTerrainData& nativeTerrain, float playerWorldX, float playerWorldY);
    bool HasNativeTerrain() const { return m_hasNativeTerrain; }
    const memory::NativeTerrainData& NativeTerrain() const { return m_nativeTerrain; }

    // Chuyển đổi tọa độ
    bool WorldToGrid(float worldX, float worldY, int32_t& outGx, int32_t& outGy) const;
    void GridToWorld(int32_t gx, int32_t gy, float& outWx, float& outWy) const;

    // Kiểm tra và gán trạng thái ô
    bool IsWalkable(float worldX, float worldY) const;
    bool IsWalkableGrid(int32_t gx, int32_t gy) const;
    void SetBlocked(float worldX, float worldY, float radius = 8.0f);
    void SetHazard(float worldX, float worldY, float radius = 12.0f);
    void SetWalkable(float worldX, float worldY, float radius = 6.0f);

    // Kiểm tra tầm nhìn thẳng (Line of Sight - LoS) bằng thuật toán Bresenham Raycast
    bool HasLineOfSight(float x1, float y1, float x2, float y2) const;

    // Tìm ô đi được gần nhất nếu vị trí đích rơi vào vật cản
    bool FindNearestWalkable(float worldX, float worldY, float& outX, float& outY, float maxSearchRadius = 40.0f) const;

    float CellSize() const { return m_cellSize; }
    float OriginX() const { return m_originX; }
    float OriginY() const { return m_originY; }
    float CenterX() const { return m_originX + (static_cast<float>(kGridDim) / 2.0f) * m_cellSize; }
    float CenterY() const { return m_originY + (static_cast<float>(kGridDim) / 2.0f) * m_cellSize; }
    uint32_t BlockedCount() const { return m_blockedCount; }

private:
    float m_cellSize;
    float m_originX = 0.0f;
    float m_originY = 0.0f;
    bool m_initialized = false;
    uint32_t m_blockedCount = 0;

    bool m_hasNativeTerrain = false;
    memory::NativeTerrainData m_nativeTerrain;

    std::vector<uint8_t> m_cells; // kGridDim * kGridDim

    inline size_t Index(int32_t gx, int32_t gy) const {
        return static_cast<size_t>(gy) * kGridDim + static_cast<size_t>(gx);
    }
};

} // namespace navigation
