#pragma once
// ── CS2 External C++ WebRadar — ReadProcessMemory Templates ──────────────────

#include <cstdint>
#include <string>
#include <windows.h>

// ── ReadProcessMemory wrapper ────────────────────────────────────────────────

template<typename T>
T rpm(HANDLE proc, std::uintptr_t addr) {
    T val{};
    ReadProcessMemory(proc, reinterpret_cast<LPCVOID>(addr), &val, sizeof(T), nullptr);
    return val;
}

// Read into existing variable — returns true on success
template<typename T>
bool rpm(HANDLE proc, std::uintptr_t addr, T& out) {
    SIZE_T read = 0;
    return ReadProcessMemory(proc, reinterpret_cast<LPCVOID>(addr),
                             &out, sizeof(T), &read) && read == sizeof(T);
}

// Read buffer
template<typename T>
bool rpm_buf(HANDLE proc, std::uintptr_t addr, T* buf, size_t count) {
    SIZE_T read = 0;
    return ReadProcessMemory(proc, reinterpret_cast<LPCVOID>(addr),
                             buf, sizeof(T) * count, &read) && read == sizeof(T) * count;
}

// ── WriteProcessMemory wrapper ──────────────────────────────────────────────

template<typename T>
bool wpm(HANDLE proc, std::uintptr_t addr, const T& val) {
    SIZE_T written = 0;
    return WriteProcessMemory(proc, reinterpret_cast<LPVOID>(addr),
                              &val, sizeof(T), &written) && written == sizeof(T);
}

// ── String read (null-terminated) ───────────────────────────────────────────

inline std::string rpm_string(HANDLE proc, std::uintptr_t addr, size_t maxLen = 128) {
    std::string result(maxLen, '\0');
    SIZE_T read = 0;
    if (!ReadProcessMemory(proc, reinterpret_cast<LPCVOID>(addr),
                           result.data(), maxLen, &read))
        return "";
    auto nullPos = result.find('\0');
    if (nullPos != std::string::npos) result.resize(nullPos);
    return result;
}

// ── Entity list resolution ─────────────────────────────────────────────────
// Matches the C# formula exactly:
//   entryIndex = (id & 0x7FFF) >> 9
//   listEntry  = Read<uintptr_t>(entityList + 8 * entryIndex + 0x10)
//   result     = Read<uintptr_t>(listEntry + 112 * (id & 0x1FF))

inline std::uintptr_t resolveEntity(HANDLE proc, std::uintptr_t entityList, int id) {
    int entryIndex = (id & 0x7FFF) >> 9;
    auto listEntry = rpm<std::uintptr_t>(proc, entityList + 8 * entryIndex + 0x10);
    if (!listEntry) return 0;
    return rpm<std::uintptr_t>(proc, listEntry + 112 * (id & 0x1FF));
}

// ── Pointer chain helper ───────────────────────────────────────────────────

struct PtrChain {
    HANDLE proc;
    std::uintptr_t current;

    PtrChain(HANDLE p, std::uintptr_t base) : proc(p), current(base) {}

    PtrChain& offset(int32_t off) {
        if (current) current += off;
        return *this;
    }

    PtrChain& deref() {
        if (current) current = rpm<std::uintptr_t>(proc, current);
        return *this;
    }

    template<typename T>
    T read(int32_t off = 0) {
        if (!current) return T{};
        return rpm<T>(proc, current + off);
    }

    explicit operator bool() const { return current != 0; }
};
