#include "Resolvers/LocalPlayerResolver.h"

#include "Offsets.h"
#include "Memory/MemoryValidation.h"

bool LocalPlayerResolver::TryResolve(VmmAccessor& mem,
                                     const RuntimeSession& session,
                                     LocalPlayerData& player)
{
    const uint32_t pid = session.processId;

    // -----------------------------------------------------------------
    // 1. Read the local player pointer from the world object
    // -----------------------------------------------------------------
    uint64_t localPlayerPointer = 0;
    if (!mem.TryReadPointer(pid,
                            session.worldAddress + Offsets::World::LocalPlayer,
                            localPlayerPointer))
    {
        m_lastFailureReason = "Failed to read LocalPlayer pointer from world";
        return false;
    }

    // -----------------------------------------------------------------
    // 2. Second hop — the actual entity sits 0xA8 bytes before the
    //    pointed-to structure.
    // -----------------------------------------------------------------
    uint64_t secondHop = 0;
    if (!mem.TryReadValue<uint64_t>(pid, localPlayerPointer + 0x8, secondHop)) {
        m_lastFailureReason = "Failed to read secondHop from LocalPlayer";
        return false;
    }

    if (secondHop <= 0xA8) {
        m_lastFailureReason = "secondHop value too small";
        return false;
    }

    uint64_t chainedEntity = secondHop - 0xA8;

    if (!MemoryValidation::IsValidUserAddress(chainedEntity)) {
        m_lastFailureReason = "chainedEntity address is not a valid user address";
        return false;
    }

    // -----------------------------------------------------------------
    // 3. VisualState pointer
    // -----------------------------------------------------------------
    uint64_t visualStateAddr = 0;
    if (!mem.TryReadPointer(pid,
                            chainedEntity + Offsets::Common::VisualState,
                            visualStateAddr))
    {
        m_lastFailureReason = "Failed to read VisualState pointer";
        return false;
    }

    // -----------------------------------------------------------------
    // 4. Position
    // -----------------------------------------------------------------
    Vector3 pos{};
    if (!mem.TryReadValue<Vector3>(pid,
                                   visualStateAddr + Offsets::Common::Position,
                                   pos))
    {
        m_lastFailureReason = "Failed to read position from VisualState";
        return false;
    }

    if (!MemoryValidation::IsValidVector(pos)) {
        m_lastFailureReason = "Position vector is not valid";
        return false;
    }

    // -----------------------------------------------------------------
    // 5. Heading from direction vector
    // -----------------------------------------------------------------
    float dirX = 0.0f;
    float dirY = 0.0f;
    mem.TryReadValue<float>(pid, visualStateAddr + Offsets::Common::DirectionX, dirX);
    mem.TryReadValue<float>(pid, visualStateAddr + Offsets::Common::DirectionY, dirY);

    float heading = 0.0f;
    MemoryValidation::TryGetCorrectedHeadingFromDirection(dirX, dirY, heading);

    // -----------------------------------------------------------------
    // 6. Commit
    // -----------------------------------------------------------------
    player = LocalPlayerData{
        .entityAddress      = chainedEntity,
        .visualStateAddress = visualStateAddr,
        .position           = pos,
        .lookDirection      = heading,
    };

    m_lastFailureReason.clear();
    return true;
}
