#pragma once

// -----------------------------------------------------------------------------
// offsets.h
// -----------------------------------------------------------------------------
// OffsetsManager: single source of truth for every game address used by the
// cheat. All offsets are resolved locally at startup - no network, no
// remote dumps:
//
//   1. Module globals (dwEntityList, local player controller, dwViewAngles,
//      dwViewMatrix, schema system, build info) are found by scanning the
//      loaded modules for byte patterns, exactly like valthrun does.
//      Patterns are re-resolved after every game restart (ASLR moves the
//      module base, the pattern match is re-computed).
//
//   2. Schema field offsets (netvars like m_iHealth, m_pGameSceneNode) are
//      taken from the game's own schema system via a live dump on every
//      start (fast enough that no cache file is needed). The dump walks the
//      same structures valthrun uses, so it automatically matches the
//      running build.
//
// The local player pawn is derived from the local player CONTROLLER slot +
// CCSPlayerController::m_hPlayerPawn + the entity list, because the pawn
// pointer global keeps changing its pattern between builds.
//
// No hard-coded offsets live in the feature code: every consumer asks this
// manager for the value it needs. The only hardcoded constants are the byte
// patterns themselves (updated on game updates) and a few offsets that the
// schema system does not expose (e.g. CModelState::m_boneArray).
// -----------------------------------------------------------------------------

#include <cstdint>
#include <string>
#include <vector>

#include "../core/memory.h"
#include "../core/schema.h"

namespace core {

// Holds every resolved game offset used by the cheat.
struct GameOffsets
{
	// client.dll globals (resolved by byte patterns).
	uintptr_t localPlayerPawn = 0;   // local player CONTROLLER slot (pawn via m_hPlayerPawn)
	uintptr_t viewAngles = 0;        // dwViewAngles (QAngle struct)
	uintptr_t entityList = 0;        // dwEntityList (pointer slot)
	uintptr_t viewMatrix = 0;        // dwViewMatrix (matrix struct)
	uintptr_t globalVars = 0;        // dwGlobalVars (Globals instance, game time)

	// engine2.dll globals (resolved by byte patterns).
	uintptr_t networkGameClient = 0; // CNetworkGameClient instance pointer slot

	// Schema netvars (from the live schema dump).
	uintptr_t hPlayerPawn = 0;           // CCSPlayerController::m_hPlayerPawn
	uintptr_t iszPlayerName = 0;         // CCSPlayerController::m_iszPlayerName
	uintptr_t idEntIndex = 0;            // C_CSPlayerPawn::m_iIDEntIndex
	uintptr_t iShotsFired = 0;           // C_CSPlayerPawn::m_iShotsFired
	uintptr_t aimPunchAngle = 0;         // C_CSPlayerPawnBase::m_aimPunchAngle (INVERTED field, legacy)
	uintptr_t aimPunchCache = 0;         // C_CSPlayerPawnBase::m_aimPunchCache (raw punch vector, legacy)
	uintptr_t aimPunchServices = 0;      // C_CSPlayerPawn::m_pAimPunchServices (newer builds)
	uintptr_t unpredictableBaseTick = 0; // CCSPlayer_AimPunchServices::m_unpredictableBaseTick
	uintptr_t teamNum = 0;               // C_BaseEntity::m_iTeamNum
	uintptr_t health = 0;                // C_BaseEntity::m_iHealth
	uintptr_t lifeState = 0;             // C_BaseEntity::m_lifeState
	uintptr_t gameSceneNode = 0;         // C_BaseEntity::m_pGameSceneNode
	uintptr_t vecVelocity = 0;           // C_BaseEntity::m_vecVelocity
	uintptr_t eyeAngles = 0;             // C_CSPlayerPawn::m_angEyeAngles
	uintptr_t entitySpottedState = 0;    // C_CSPlayerPawn::m_entitySpottedState
	uintptr_t spotted = 0;               // EntitySpottedState_t::m_bSpotted
	uintptr_t oldOrigin = 0;             // C_BasePlayerPawn::m_vOldOrigin
	uintptr_t vecAbsOrigin = 0;          // CSkeletonInstance::m_vecAbsOrigin
	uintptr_t vecViewOffset = 0;         // C_BaseModelEntity::m_vecViewOffset
	uintptr_t modelState = 0;            // CSkeletonInstance::m_modelState
	uintptr_t boneArray = 0;             // CModelState::m_boneArray (bundled constant)
	uintptr_t hModel = 0;                // CModelState::m_hModel
	uintptr_t glow = 0;                  // C_BaseModelEntity::m_Glow
	uintptr_t glowType = 0;              // CGlowProperty::m_iGlowType
	uintptr_t glowColorOverride = 0;     // CGlowProperty::m_glowColorOverride
	uintptr_t glowFlashing = 0;          // CGlowProperty::m_bFlashing
	uintptr_t glowTime = 0;              // CGlowProperty::m_flGlowTime
	uintptr_t glowStartTime = 0;         // CGlowProperty::m_flGlowStartTime
	uintptr_t glowGlowing = 0;           // CGlowProperty::m_bGlowing
	uintptr_t flashMaxAlpha = 0;         // C_CSPlayerPawnBase::m_flFlashMaxAlpha
	uintptr_t flashDuration = 0;         // C_CSPlayerPawnBase::m_flFlashDuration
	uintptr_t isDefusing = 0;            // C_CSPlayerPawnBase::m_bIsDefusing (moved to C_CSPlayerPawn)
	uintptr_t c4SpottedState = 0;        // C_C4::m_entitySpottedState (carried/dropped bomb)
	uintptr_t plantedC4SpottedState = 0; // C_PlantedC4::m_entitySpottedState (planted bomb)
	uintptr_t hOwnerEntity = 0;          // C_BaseEntity::m_hOwnerEntity (C4 owner handle)
	uintptr_t plantedC4BombSite = 0;     // C_PlantedC4::m_nBombSite (0 = A, 1 = B)
	uintptr_t plantedC4Blow = 0;         // C_PlantedC4::m_flC4Blow (detonation server time)
	uintptr_t plantedC4TimerLength = 0;  // C_PlantedC4::m_flTimerLength (full countdown seconds)
	uintptr_t plantedC4BombDefused = 0;  // C_PlantedC4::m_bBombDefused
	uintptr_t plantedC4HasExploded = 0;  // C_PlantedC4::m_bHasExploded
	uintptr_t serverTickCount = 0;       // CNetworkGameClient::m_serverTickCount (engine2)
	uintptr_t mapName = 0;               // CNetworkGameClient::m_szMapName (web radar)

	// Returns true when the essential offsets were resolved.
	bool IsComplete() const;
};

// Loads and resolves all game offsets for the current process.
class OffsetsManager
{
public:
	// Resolves every game offset locally: module globals via byte pattern
	// scanning, schema netvars from a live schema dump. No network access.
	bool Initialize(const ProcessMemory& memory);

	// Returns the resolved offsets (valid only after Initialize()).
	const GameOffsets& Get() const;

	// Build datetime shown in the menu.
	std::string GetBuildDatetime() const;

	// Re-resolves dwEntityList by scanning client.dll. Public so the main
	// loop can re-resolve it after the game restarts (client.dll is then
	// loaded at a new ASLR base).
	bool ResolveEntityListBySignature(const ProcessMemory& memory);

	// Returns the engine2.dll base resolved at initialization (0 when not
	// available). engine2-relative offsets need this base to compute
	// absolute addresses.
	uintptr_t GetEngineBase() const;

private:
	// Resolves every module global (entity list, local controller, view
	// angles, view matrix, schema system, build info) from byte patterns.
	// Returns false when a required pattern did not match.
	bool ResolveGlobalsBySignature(const ProcessMemory& memory);

	// Reads the build datetime from the engine build info global (shown in
	// the menu). Returns false when unavailable.
	bool ResolveBuildInfo(const ProcessMemory& memory);

	// Dumps the schema from memory and applies the result to m_offsets.
	// Returns false on failure.
	bool LoadOrDumpSchema(const ProcessMemory& memory);

	// Fills m_offsets from the schema offset entries (class-scoped lookups).
	// client.dll wins over !GlobalTypes on duplicate (class, member) keys.
	// Field owners move between builds, so several fields are looked up over
	// candidate classes in order (first hit wins): m_vecAbsOrigin is
	// DECLARED on CGameSceneNode and only inherited by CSkeletonInstance
	// (CGameSceneNode must be tried first); m_bIsDefusing moved from
	// C_CSPlayerPawnBase to C_CSPlayerPawn in Aug 2026. Missing fields stay
	// 0 and the consumers fall back gracefully; m_serverTickCount and
	// m_szMapName get bundled fallbacks (0x24C / 0x218) when absent.
	void ApplySchemaOffsets(const std::vector<SchemaOffsetEntry>& offsets);

private:
	GameOffsets m_offsets;
	std::string m_buildDatetime;

	// Module-relative offsets of the pattern-resolved globals.
	uintptr_t m_schemaSystemOffset = 0;
	uintptr_t m_buildInfoOffset = 0;
	uintptr_t m_engineBase = 0;        // engine2.dll base (for engine2 offsets)
};

} // namespace core
