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

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

// -------------------------------------------------------------------------
// BulletTableReader
// Reads the world's active bullet table each call (non-incremental).
// Direction is derived from the visual state's stored direction vectors;
// if those are unavailable the reader falls back to computing a flight
// delta from the previous frame's position.
// -------------------------------------------------------------------------

class BulletTableReader {
public:
    /// Read all active bullets into @p entries.
    /// Returns false only when the bullet table pointer cannot be read.
    bool TryRead(VmmAccessor&                    mem,
                 const RuntimeSession&           session,
                 std::vector<DayZBulletEntry>&   entries);

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

private:
    // ------------------------------------------------------------------
    // Per-bullet state (previous-frame position for flight-delta fallback)
    // ------------------------------------------------------------------
    std::unordered_map<uint64_t, Vector3> m_lastPositions;

    bool m_tableUnavailable = false;

    // ------------------------------------------------------------------
    // Entry construction
    // ------------------------------------------------------------------
    DayZBulletEntry BuildBulletEntry(VmmAccessor&           mem,
                                     const RuntimeSession&  session,
                                     uint64_t               address);

    /// Read the bullet's current position from its visual state.
    static bool TryReadBulletPosition(VmmAccessor& mem,
                                      uint32_t     pid,
                                      uint64_t     visualStateAddr,
                                      Vector3&     pos);

    /// Read the stored direction vectors (DirectionX / DirectionY) from the
    /// visual state and derive a normalized world direction + heading.
    static bool TryReadBulletDirection(VmmAccessor& mem,
                                       uint32_t     pid,
                                       uint64_t     visualStateAddr,
                                       Vector3&     dir,
                                       float&       heading);

    /// Derive flight direction from the delta between previous and current
    /// positions.  Returns false when the delta is too small to be useful.
    bool TryGetFlightDirection(const Vector3& prev,
                               const Vector3& curr,
                               Vector3&       dir,
                               float&         heading);

    /// Sync m_lastPositions with the entries that were just built:
    /// add/update entries still alive, remove entries no longer present.
    void UpdateLastPositions(const std::vector<DayZBulletEntry>& entries);

    // ------------------------------------------------------------------
    // Constants
    // ------------------------------------------------------------------
    static constexpr int   kMaxBullets        = 512;
    static constexpr float kMinDirectionDelta = 0.001f;
};
