#pragma once
#include <vector>
#include <unordered_map>
#include <chrono>

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

// -------------------------------------------------------------------------
// FarEntityListReader
// Incremental reader for the world's far-entity pointer table.
// Processes entities in time-budgeted batches across calls, maintains a
// cached result set, and prunes stale entries by timestamp.
// -------------------------------------------------------------------------

class FarEntityListReader {
public:
    FarEntityListReader(int batchSize    = 64,
                        int readBudgetMs = 5,
                        int staleAfterMs = 5000);

    /// Incrementally read far entities into @p entries.
    /// Returns false only when the table pointer itself cannot be read.
    bool TryRead(VmmAccessor&                      mem,
                 const RuntimeSession&             session,
                 std::vector<DayZFarEntityEntry>&  entries);

    /// Position-only scatter refresh: re-reads positions for all entities
    /// already known from previous TryRead calls without advancing the cursor.
    /// Call this every frame / playersRefreshMs for smooth movement.
    void RefreshPositions(VmmAccessor& mem, const RuntimeSession& session);

    /// Reset all state. Call when the game session restarts.
    void Reset();

private:
    // ------------------------------------------------------------------
    // Per-entity bookkeeping
    // ------------------------------------------------------------------
    struct CachedEntity {
        DayZFarEntityEntry                     entry;
        uint64_t                               visualStateAddr      = 0;  // cached for RefreshPositions
        int                                    lastSeenGeneration   = 0;
        std::chrono::steady_clock::time_point  lastSuccessfulRead;
        int                                    failedTypeReads      = 0;
    };

    // ------------------------------------------------------------------
    // Internal helpers
    // ------------------------------------------------------------------
    void ProcessEntity(VmmAccessor&             mem,
                       const RuntimeSession&    session,
                       uint64_t                 entityAddr,
                       std::chrono::steady_clock::time_point now);

    void CompleteScan(std::chrono::steady_clock::time_point now);

    void PruneExpiredEntities(std::chrono::steady_clock::time_point now);

    std::vector<DayZFarEntityEntry>& BuildResult();

    void MarkTypeReadFailure(uint64_t                              entityAddr,
                             CachedEntity*                         cached,
                             std::chrono::steady_clock::time_point now);

    // ------------------------------------------------------------------
    // State
    // ------------------------------------------------------------------
    std::unordered_map<uint64_t, CachedEntity> m_entities;
    EntityTypeCache                            m_typeCache;
    std::vector<DayZFarEntityEntry>            m_cachedResult;
    std::vector<uint64_t>                      m_sortedAddresses;

    int  m_cursorOffset       = 0;
    int  m_scanGeneration     = 1;
    bool m_hasCompletedScan   = false;
    bool m_sortDirty          = true;
    bool m_resultDirty        = true;

    int                         m_batchSize;
    std::chrono::milliseconds   m_readBudget;
    std::chrono::milliseconds   m_staleAfter;

    static constexpr int kMaxEntities = 4096;
};
