#pragma once

// -----------------------------------------------------------------------------
// memory.h
// -----------------------------------------------------------------------------
// ProcessMemory: thin wrapper around the Win32 process memory APIs
// (ReadProcessMemory / WriteProcessMemory / CreateToolhelp32Snapshot).
// Provides typed reads/writes, array access, string reads and byte-pattern
// scanning. All methods are process agnostic: the handle is set once by
// Attach() and reused by every feature thread.
// -----------------------------------------------------------------------------

#include <windows.h>
#include <cstdint>
#include <string>

namespace core {

// Describes a loaded module of the target process.
struct ModuleInfo
{
	uintptr_t base;   // Module base address.
	size_t size;      // Module image size.
};

// Wrapper for RPM/WPM against a single target process.
class ProcessMemory
{
public:
	ProcessMemory() = default;

	// Opens the first process whose executable name matches (e.g. "cs2.exe").
	bool Attach(const std::string& processName);

	// Closes the process handle.
	void Detach();

	// Returns true when a valid process handle is open.
	bool IsAttached() const;

	// Returns true while the attached process still runs (false when the
	// process exited or no process is attached).
	bool IsProcessAlive() const;

	// Returns the PID of the attached process (0 when not attached).
	DWORD GetPid() const;

	// Looks up a module by name and returns its base address and size.
	ModuleInfo GetModule(const std::string& moduleName) const;

	// Reads a single value of type T from the given address.
	template <typename T>
	T Read(uintptr_t address) const
	{
		T value{};
		SIZE_T bytesRead = 0;
		ReadProcessMemory(m_process, reinterpret_cast<LPCVOID>(address), &value, sizeof(T), &bytesRead);
		return value;
	}

	// Reads `count` elements of type T into the caller-provided buffer.
	template <typename T>
	void ReadArray(uintptr_t address, T* buffer, size_t count) const
	{
		SIZE_T bytesRead = 0;
		ReadProcessMemory(m_process, reinterpret_cast<LPCVOID>(address), buffer,
			sizeof(T) * count, &bytesRead);
	}

	// Writes a single value of type T to the given address.
	template <typename T>
	void Write(uintptr_t address, const T& value) const
	{
		SIZE_T bytesWritten = 0;
		WriteProcessMemory(m_process, reinterpret_cast<LPVOID>(address), &value, sizeof(T), &bytesWritten);
	}

	// Writes `count` elements of type T from the caller-provided buffer.
	template <typename T>
	void WriteArray(uintptr_t address, const T* buffer, size_t count) const
	{
		SIZE_T bytesWritten = 0;
		WriteProcessMemory(m_process, reinterpret_cast<LPVOID>(address), buffer,
			sizeof(T) * count, &bytesWritten);
	}

	// Reads an ASCII string of up to `maxLength` bytes (stops at NUL).
	std::string ReadString(uintptr_t address, size_t maxLength) const;

	// Scans [start, start+length) for a byte pattern.
	// mask uses 'x' for exact match and '?' for wildcard.
	uintptr_t FindPattern(uintptr_t start, size_t length,
		const char* pattern, const char* mask) const;

private:
	HANDLE m_process = INVALID_HANDLE_VALUE;
	DWORD m_pid = 0;
};

} // namespace core