#pragma once
#include <cstdint>
#include <cstring>
#include <vector>
#include <string>

// ─────────────────────────────────────────────────────────────────────────────
// Lightweight in-memory chunk reader for WoW chunked binary formats
// (WMO, ADT, WDT, etc.)
// ─────────────────────────────────────────────────────────────────────────────

struct WoWChunk {
    uint32_t magic  = 0;
    uint32_t size   = 0;
    const uint8_t *data = nullptr; // points into the owning buffer

    // Compare magic as 4 chars (e.g. "MVER").
    // WoW stores chunk IDs reversed in the file (MVER → REVM bytes).
    // After memcpy into uint32_t LE the logical name sits MSB→LSB,
    // so we compare each nibble from the top.
    bool is(const char *tag) const {
        return ((magic >> 24) & 0xFF) == (uint8_t)tag[0] &&
               ((magic >> 16) & 0xFF) == (uint8_t)tag[1] &&
               ((magic >>  8) & 0xFF) == (uint8_t)tag[2] &&
               ((magic >>  0) & 0xFF) == (uint8_t)tag[3];
    }

    template<typename T>
    const T &as() const { return *reinterpret_cast<const T *>(data); }

    template<typename T>
    const T *array() const { return reinterpret_cast<const T *>(data); }

    uint32_t count_of(size_t elem_size) const {
        return size / (uint32_t)elem_size;
    }
};

// Reads all top-level chunks from a buffer.
// For WMO groups, use ChunkReader on the MOGP payload to get sub-chunks.
class ChunkReader {
public:
    explicit ChunkReader(const uint8_t *buf, size_t len)
        : m_buf(buf), m_len(len) {}

    // Returns true if chunk at current position was read into `out`
    bool next(WoWChunk &out) {
        if (m_pos + 8 > m_len) return false;
        std::memcpy(&out.magic, m_buf + m_pos, 4);
        std::memcpy(&out.size,  m_buf + m_pos + 4, 4);
        m_pos += 8;
        if (m_pos + out.size > m_len) return false;
        out.data = m_buf + m_pos;
        m_pos += out.size;
        return true;
    }

    bool eof() const { return m_pos >= m_len; }
    void reset()     { m_pos = 0; }

    // Find first chunk with given 4-char magic, reset on miss
    bool find(const char *tag, WoWChunk &out) {
        reset();
        WoWChunk c;
        while (next(c)) {
            if (c.is(tag)) { out = c; return true; }
        }
        return false;
    }

private:
    const uint8_t *m_buf;
    size_t         m_len;
    size_t         m_pos = 0;
};

// ─── Helpers ──────────────────────────────────────────────────────────────────

// WoW world coordinates → Godot Y-up
// WoW: X=North, Y=West, Z=Up, world center at (17066.666, 17066.666)
inline void wow_to_godot(float wx, float wy, float wz,
                          float &gx, float &gy, float &gz)
{
    gx = -(wy - 17066.666f);
    gy =   wz;
    gz = -(wx - 17066.666f);
}

// WoW MDDF/MODF rotation (degrees, render-space Y-up: rx=pitch, ry=yaw, rz=roll)
// → Godot Euler (radians). WoW's "0 yaw" differs from Godot's, so ry needs
// a fixed +90° offset (matches Blender wow.export importer).
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
inline void wow_rot_to_godot(float rx, float ry, float rz,
                              float &gx, float &gy, float &gz)
{
    constexpr float D2R = (float)(M_PI / 180.0);
    gx =  rx          * D2R;
    gy = (ry - 90.0f) * D2R;
    gz =  rz          * D2R;
}

// Collect null-separated strings from a chunk into a vector
inline std::vector<std::string> parse_string_block(const WoWChunk &c) {
    std::vector<std::string> result;
    const char *p   = reinterpret_cast<const char *>(c.data);
    const char *end = p + c.size;
    while (p < end) {
        result.emplace_back(p);
        p += result.back().size() + 1;
    }
    return result;
}
