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

#include "Core/Models.h"
#include "Core/RuntimeSession.h"
#include "Memory/VmmAccessor.h"

// -------------------------------------------------------------------------
// EntityTypeMetadata
// All type-level strings read from the entity's type object.
// -------------------------------------------------------------------------

struct EntityTypeMetadata {
    std::string typeName;
    std::string configName;
    std::string cleanName;
    std::string modelName;
};

// -------------------------------------------------------------------------
// EntityTypeCache
// In-process cache keyed by type object address.
// -------------------------------------------------------------------------

class EntityTypeCache {
    std::unordered_map<uint64_t, EntityTypeMetadata> m_cache;
public:
    /// Return true (and populate @p out) if the address is already cached.
    bool GetOrRead(uint64_t typeAddress, EntityTypeMetadata& out) {
        auto it = m_cache.find(typeAddress);
        if (it != m_cache.end()) { out = it->second; return true; }
        return false;
    }

    void Set(uint64_t typeAddress, const EntityTypeMetadata& meta) {
        m_cache[typeAddress] = meta;
    }

    void Clear() { m_cache.clear(); }
};

// -------------------------------------------------------------------------
// Free functions implemented in EntityTypeCache.cpp
// -------------------------------------------------------------------------

/// Format a raw PascalCase entity name into a human-readable string.
/// Strips prefixes like "Dayz_", splits on capital letter boundaries,
/// and replaces underscores with spaces.
/// e.g. "KizlyarKnife" → "Kizlyar Knife", "CivilianSedan" → "Civilian Sedan"
std::string FormatEntityName(const std::string& raw);

/// Sanitize an entity type string read from memory.
/// Returns empty string if the value contains control characters or the
/// fraction of readable characters (letters/digits/whitespace/punctuation) is
/// below 50 %.
std::string SanitizeEntityString(const std::string& value);

/// Pick the best display name from the four candidate strings.
/// Falls back through typeName -> configName -> cleanName -> modelName, then
/// to a hex address string if all are empty.
std::string GetBestEntityName(const std::string& typeName,
                               const std::string& configName,
                               const std::string& cleanName,
                               const std::string& modelName,
                               uint64_t            entityAddress);

/// Read a single type string: dereference ptr at typeAddress+offset, then
/// call ReadArmaString on the resulting pointer.
std::string ReadEntityTypeString(VmmAccessor& mem,
                                 uint32_t     pid,
                                 uint64_t     typeAddress,
                                 uint64_t     offset);

/// Read all four type strings for a type object and return the populated
/// EntityTypeMetadata.
EntityTypeMetadata ReadEntityTypeMetadata(VmmAccessor& mem,
                                          uint32_t     pid,
                                          uint64_t     typeAddress);

/// Read the entity transform (position + heading) from the entity's
/// VisualState.  Both outputs are optional; if a read or validation step
/// fails the corresponding optional remains empty.
/// Returns true if at least position was read successfully.
bool ReadEntityTransform(VmmAccessor&          mem,
                         const RuntimeSession& session,
                         uint64_t              entityAddress,
                         std::optional<Vector3>& position,
                         std::optional<float>&   headingDegrees);
