#pragma once
#include <winsock2.h>
#include <Windows.h>
#include <cstdint>
#include <cstddef>
#include <functional>

// if you change this, make sure 
// to change it in the client also...
#define PACKET_DATA_SIZE 0x1000

// max symbol string size... if you 
// change this update the client also...
#define PACKET_SYMBOL_SIZE 0x1000

namespace theo
{
	enum class theo_packet_type
	{
		init,
		alloc_memory,
		resolve_symbol,
		copy_memory,
		disconnect
	};

	enum class theo_file_type
	{
		demo_drv,
		demo_dll,
		demo_imgui
	};

#pragma pack(push, 1)
	typedef struct _theo_data
	{
		theo_packet_type type;

		union
		{
			theo_file_type file;
			std::uintptr_t entry_point;

			struct
			{
				void* addr;
				std::size_t alloc_size;
				std::uint32_t prot;
			} alloc;

			struct
			{
				void* dest_addr;
				std::size_t size;
				std::uint8_t data[PACKET_DATA_SIZE];
			} copy_memory;

			struct
			{
				std::uintptr_t symbol_addr;
				std::size_t symbol_size;
				char symbol[PACKET_SYMBOL_SIZE];
			} resolve;
		};
	} theo_data, * ptheo_data;
#pragma pack(pop)

	using malloc_t = std::function<void*(std::size_t, std::uint32_t)>;
	using memcpy_t = std::function<decltype(memcpy)>;

	using resolve_symbol_t = std::function<std::uintptr_t(const char*)>;
	using mapper_routines_t = std::tuple<malloc_t, memcpy_t, resolve_symbol_t>;

	class client
	{
	public:
		explicit client
		(
			SOCKET client_socket, 
			const theo::theo_data& init_packet, 
			const mapper_routines_t& routines
		);

		// returns entry point...
		std::uintptr_t handle() const;
	private:
		SOCKET client_socket;
		malloc_t alloc;
		memcpy_t mcopy;
		resolve_symbol_t resolver;
	};
}