#pragma once

// -----------------------------------------------------------------------------
// entitylist.h
// -----------------------------------------------------------------------------
// EntityList: traversal of the CS2 entity list (the "game entity system"
// list) and classification of pawn entities. Slot semantics match the
// original cheat: slots 0..63 are player controller indices that resolve to
// pawns via m_hPlayerPawn, while entity indices >= 64 (crosshair ids) are
// pawn entities directly.
//
// Memory layout (validated against the live runtime):
//   dwEntityList -> outer[64] -> inner[512 * 0x70] -> identity
//   identity + 0x08 -> classInfo -> +0x08 -> binding -> +0x08 -> class name
// -----------------------------------------------------------------------------

#include <cstdint>
#include <string>

#include "../core/memory.h"
#include "../core/offsets.h"
#include "vector.h"

namespace sdk {

// Maximum number of players; the entity list is only scanned over the
// 64 player controller slots (a CS2 match never has more than 64 players).
constexpr int kMaxPlayers = 64;

// Maximum raw entity index. The entity list supports 32768 identities
// (64 chunks x 512 entries, matching the 15-bit entity index in handles);
// guarding at 16384 would make every entity with an index >= 16384
// invisible to the cheat.
constexpr int kMaxRawEntities = 32768;

// Traverses the entity list of the target process.
class EntityList
{
public:
	// Binds the memory reader, offset table and client.dll base address.
	void Initialize(const core::ProcessMemory* memory, const core::GameOffsets* offsets, uintptr_t clientBase);

	// Resolves the C_CSPlayerPawn of a slot. Slots < 64 go through the
	// player controller (m_hPlayerPawn), slots >= 64 are pawn entities
	// (crosshair ids), accessed directly at their raw index.
	uintptr_t GetPawn(int slot) const;

	// Returns the entity pointer at a raw identity index (no class check).
	uintptr_t GetRawEntity(int entityIndex) const;

	// Returns the address of the identity slot for a raw entity index
	// (the entity pointer lives at identity + 0). Used for cheap
	// re-validation of cached pawn pointers.
	uintptr_t GetIdentityAddress(int entityIndex) const;

	// Resolves a controller slot's pawn and the address of the pawn's
	// identity slot in one call (same chain as GetPawn). Callers can
	// re-validate the returned pawn every frame with a single read.
	bool GetPawnWithIdentity(int slot, uintptr_t& outPawn, uintptr_t& outIdentity) const;

	// Returns the class name of the entity at a raw identity index.
	std::string GetEntityClassName(int entityIndex) const;

	// Returns the first raw entity index whose class name matches, or -1
	// when none does. Walks the 32768 identities chunk-by-chunk with one
	// read per identity (class chain only for live entities), so a full
	// scan stays cheap enough for a 1 Hz refresh. Used to locate the bomb
	// entities (C_C4 / C_PlantedC4).
	int FindEntityByClassName(const char* className) const;

	// Returns true when the pawn is a valid, alive player (team 2/3,
	// not on the local team, health 1..100, lifeState alive). This is the
	// shared filter used by every feature before touching a pawn.
	bool IsValidPawn(uintptr_t pawn, int localTeam) const;

	// Returns true when the slot holds a valid, alive enemy pawn.
	bool IsValidEnemy(int slot, int localTeam) const;

	// Returns the team number of the pawn in the given slot.
	int GetTeam(int slot) const;

	// Returns the world position of the pawn's origin (m_vOldOrigin).
	bool GetOrigin(int slot, Vec3& outOrigin) const;

	// Returns the world position of a bone of the pawn in the slot.
	bool GetBonePosition(int slot, int boneIndex, Vec3& outPosition) const;

	// Resolves a bone index by name from the pawn's skeleton model.
	int FindBoneByName(uintptr_t pawn, const char* boneName) const;

private:
	const core::ProcessMemory* m_memory = nullptr;
	const core::GameOffsets* m_offsets = nullptr;
	uintptr_t m_clientBase = 0;
};

} // namespace sdk