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

#include "Memory/VmmAccessor.h"

// -----------------------------------------------------------------------
// SigScanner
//
// Reads the game module in chunks via DMA and searches for byte patterns
// to extract offsets that change with each game update.
//
// Supported scan types:
//   MovCs   — RIP-relative MOV/LEA: read int32 disp from the first wildcard
//             position, resolve to a module-relative RVA.
//   MovReg  — Struct-member access: read uint32 from the first wildcard
//             position; the value IS the member offset within the struct.
//
// Usage:
//   auto result = SigScanner::Scan(mem, pid, moduleBase);
//   SigScanner::Apply(result);   // writes into RuntimeOffsets
// -----------------------------------------------------------------------
class SigScanner {
public:
    struct ScanResult {
        // ------------------------------------------------------------------
        // Base:: — module-relative RVAs of global-variable slots
        // (MovCs — resolved from RIP-relative displacement)
        // ------------------------------------------------------------------
        std::optional<uint64_t> baseWorld;           // Base::World
        std::optional<uint64_t> baseNetworkManager;  // Base::NetworkManager

        // ------------------------------------------------------------------
        // World:: — struct member offsets within the World object
        // (MovReg — 4-byte little-endian value at the first wildcard pos)
        // ------------------------------------------------------------------
        std::optional<uint32_t> worldNearEntList;    // World::NearEntityList
        std::optional<uint32_t> worldFarEntList;     // World::FarEntityList
        std::optional<uint32_t> worldBulletList;     // World::BulletTable
        std::optional<uint32_t> worldCamera;         // World::Camera
        std::optional<uint32_t> worldItemList;       // World::ItemList

        // ------------------------------------------------------------------
        // Entity struct offsets (MovReg)
        // ------------------------------------------------------------------
        std::optional<uint32_t> humanVisualState;    // Common::VisualState
        std::optional<uint32_t> humanIsDead;         // Player::IsDead
        std::optional<uint32_t> playerInventory;     // Inventory::Base
        std::optional<uint32_t> inventoryHands;      // Inventory::Hands
    };

    // Scan the game module for all configured patterns.
    // moduleBase: runtime VA of the module's first mapped byte.
    static ScanResult Scan(VmmAccessor& mem, uint32_t pid, uint64_t moduleBase);

    // Write scanned values into RuntimeOffsets, logging each change.
    static void Apply(const ScanResult& result);

private:
    struct PatternByte { uint8_t value; bool wildcard; };

    // Parse a hex pattern string like "4C 8B 05 ? ? ? ? 48" into PatternByte vec.
    static std::vector<PatternByte> ParsePattern(const std::string& hex);

    // Return the byte-offset of the first match in [haystack], or nullopt.
    static std::optional<size_t> FindFirst(const std::vector<uint8_t>&  haystack,
                                           const std::vector<PatternByte>& pattern,
                                           size_t startPos = 0);

    // Read a 4-byte little-endian uint32 from bytes[offset].
    static uint32_t ReadU32(const std::vector<uint8_t>& bytes, size_t offset);

    // For MovCs: target = matchRva + instrLen + (int32_t)disp32
    static uint64_t ResolveRip(uint64_t matchRva, int instrLen, uint32_t disp32Raw);

    // For MovReg: return the uint32 at the first wildcard position in the pattern.
    static uint32_t ExtractMovReg(const std::vector<uint8_t>&     chunk,
                                   size_t                          matchPos,
                                   const std::vector<PatternByte>& pattern);

    // Read SizeOfImage from the module's PE optional header.
    static size_t ReadModuleSize(VmmAccessor& mem, uint32_t pid, uint64_t moduleBase);
};
