#include "memory/terrain_reader.hpp"

#include <algorithm>
#include <iostream>

namespace memory {

bool TerrainReader::DecodeWalkableBuffer(
    const uint8_t* rawData,
    size_t rawSize,
    uint32_t cols,
    uint32_t rows,
    uint32_t bytesPerRow,
    std::vector<uint8_t>& outWalkability
) {
    if (!rawData || cols == 0 || rows == 0) return false;

    const size_t totalCells = static_cast<size_t>(cols) * rows;
    outWalkability.resize(totalCells, 0);

    const uint32_t pitch = (bytesPerRow >= cols) ? bytesPerRow : cols;

    // Trường hợp 1: Định dạng 1 byte trên mỗi ô (PoE standard melee layer)
    if (rawSize >= static_cast<size_t>(rows) * pitch) {
        for (uint32_t y = 0; y < rows; ++y) {
            const size_t rowOffset = static_cast<size_t>(y) * pitch;
            for (uint32_t x = 0; x < cols; ++x) {
                const uint8_t val = rawData[rowOffset + x];
                // Trong engine PoE, các ô đi được có bit 0 hoặc giá trị khác 0
                outWalkability[static_cast<size_t>(y) * cols + x] = (val != 0) ? 1 : 0;
            }
        }
        return true;
    }

    // Trường hợp 2: Định dạng nén 2 ô / byte (4-bit nibbles)
    const size_t packedPitch = (pitch + 1) / 2;
    if (rawSize >= static_cast<size_t>(rows) * packedPitch) {
        for (uint32_t y = 0; y < rows; ++y) {
            const size_t rowOffset = static_cast<size_t>(y) * packedPitch;
            for (uint32_t x = 0; x < cols; ++x) {
                const uint8_t byteVal = rawData[rowOffset + (x / 2)];
                const uint8_t nibble = (x % 2 == 0) ? (byteVal & 0x0F) : ((byteVal >> 4) & 0x0F);
                outWalkability[static_cast<size_t>(y) * cols + x] = (nibble != 0) ? 1 : 0;
            }
        }
        return true;
    }

    // Trường hợp 3: Định dạng bitmask 1 bit / ô (1 bit per cell)
    const size_t bitPitch = (cols + 7) / 8;
    if (rawSize >= static_cast<size_t>(rows) * bitPitch) {
        for (uint32_t y = 0; y < rows; ++y) {
            const size_t rowOffset = static_cast<size_t>(y) * bitPitch;
            for (uint32_t x = 0; x < cols; ++x) {
                const uint8_t byteVal = rawData[rowOffset + (x / 8)];
                const bool isWalkable = ((byteVal >> (x % 8)) & 1) != 0;
                outWalkability[static_cast<size_t>(y) * cols + x] = isWalkable ? 1 : 0;
            }
        }
        return true;
    }

    return false;
}

bool TerrainReader::ExtractFromTerrainStruct(
    IMemoryReader& reader,
    uintptr_t terrainStructAddr,
    NativeTerrainData& outTerrain
) {
    if (terrainStructAddr == 0) return false;

    // Đọc khối thông tin header của TerrainData (thường trong khoảng 0x100 bytes)
    std::vector<uint8_t> header(0x100, 0);
    if (!reader.Read(terrainStructAddr, header.data(), header.size())) {
        return false;
    }

    // Dò tìm cặp uint32 kích thước (cols, rows) trong header
    // PoE map dimensions thường nằm trong dải [60, 3500]
    for (size_t offset = 0; offset + 8 <= header.size(); offset += 4) {
        const uint32_t c = *reinterpret_cast<const uint32_t*>(&header[offset]);
        const uint32_t r = *reinterpret_cast<const uint32_t*>(&header[offset + 4]);

        if (c >= 60 && c <= 3500 && r >= 60 && r <= 3500) {
            // Đã tìm thấy cặp kích thước hợp lệ! Tìm tiếp con trỏ bộ đệm va chạm gần đó
            for (size_t ptrOff = 0; ptrOff + 8 <= header.size(); ptrOff += 8) {
                const uintptr_t bufPtr = *reinterpret_cast<const uintptr_t*>(&header[ptrOff]);
                if (bufPtr < 0x10000 || bufPtr > 0x7FFFFFFEFFFF) continue;

                const size_t expectedBytes = static_cast<size_t>(c) * r;
                std::vector<uint8_t> sampleBuf(std::min<size_t>(expectedBytes, 16384), 0);
                if (reader.Read(bufPtr, sampleBuf.data(), sampleBuf.size())) {
                    // Đọc thành công mẫu! Tiến hành đọc toàn bộ mảng dữ liệu
                    std::vector<uint8_t> fullBuf(expectedBytes, 0);
                    if (reader.Read(bufPtr, fullBuf.data(), fullBuf.size())) {
                        NativeTerrainData terrain;
                        terrain.cols = c;
                        terrain.rows = r;
                        terrain.bytesPerRow = c;
                        terrain.gridToWorldScale = 10.875f;

                        // INV-TERRAIN-ORIGIN: Dò tìm tọa độ origin gốc (float X, Y) trong header terrain struct
                        for (size_t fOff = 0; fOff + 8 <= header.size(); fOff += 4) {
                            const float fx = *reinterpret_cast<const float*>(&header[fOff]);
                            const float fy = *reinterpret_cast<const float*>(&header[fOff + 4]);
                            if (std::isfinite(fx) && std::isfinite(fy) &&
                                std::fabs(fx) >= 10.0f && std::fabs(fx) <= 100000.0f &&
                                std::fabs(fy) >= 10.0f && std::fabs(fy) <= 100000.0f) {
                                terrain.worldOriginX = fx;
                                terrain.worldOriginY = fy;
                                break;
                            }
                        }

                        if (DecodeWalkableBuffer(fullBuf.data(), fullBuf.size(), c, r, c, terrain.walkability)) {
                            const float pct = terrain.WalkablePercentage();
                            // Một bản đồ PoE hợp lệ thường có tỷ lệ walkable từ 8% đến 92%
                            if (pct >= 8.0f && pct <= 92.0f) {
                                outTerrain = std::move(terrain);
                                return true;
                            }
                        }
                    }
                }
            }
        }
    }

    return false;
}

bool TerrainReader::ScanTerrainCandidates(
    IMemoryReader& reader,
    uintptr_t baseAddr,
    size_t searchRangeBytes,
    NativeTerrainData& outTerrain
) {
    if (baseAddr == 0 || searchRangeBytes == 0) return false;

    // Giới hạn phạm vi quét tối đa 64 KB để đảm bảo tốc độ
    const size_t scanLen = std::min<size_t>(searchRangeBytes, 65536);
    std::vector<uint8_t> scanBuf(scanLen, 0);
    if (!reader.Read(baseAddr, scanBuf.data(), scanLen)) {
        return false;
    }

    // Quét tìm các con trỏ trỏ tới cấu trúc TerrainData hợp lệ
    for (size_t i = 0; i + 8 <= scanLen; i += 8) {
        const uintptr_t candPtr = *reinterpret_cast<const uintptr_t*>(&scanBuf[i]);
        if (candPtr < 0x10000 || candPtr > 0x7FFFFFFEFFFF) continue;

        if (ExtractFromTerrainStruct(reader, candPtr, outTerrain)) {
            return true;
        }
    }

    return false;
}

} // namespace memory
