#pragma once

#include "PacketHandlerDispatcherInstance.h"
#include "Utils/MemoryUtils.h"
#include "ActorRuntimeID.h"
#include "ContainerID.h"
#include "ContainerType.h"
#include "ActorUniqueID.h"

enum class PacketID : int {
	Text = 9,
	RemoveActor = 14,
	MobEffect = 28,
	MobEquipment = 31,
	Interact = 33,
	SetActorMotion = 40,
	ContainerOpen = 46,
	ContainerClose = 47,
	PlayerAuthInput = 144,
};

class Packet {
public:
	uintptr_t** vTable;

	auto getHandler() -> PacketHandlerDispatcherInstance*;
	auto getID() -> PacketID;

	static auto CreatePacket(PacketID) -> std::shared_ptr<Packet>;
};

class TextPacket : public Packet {
public:
	enum Type {
		Raw = 0,
		Chat = 1,
		Translate = 2,
		Popup = 3,
		JukeboxPopup = 4,
		Tip = 5,
		SystemMessage = 6,
		Whisper = 7,
		Announcement = 8,
		TextObjectWhisper = 9,
		TextObject = 10,
		TextObjectAnnouncement = 11,
	};
	TextPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::Text).get()->vTable;
	}

	auto getType() -> Type;
	auto setType(Type) -> void;
	auto getName() -> std::string;
	auto setName(std::string) -> void;
	auto getMessage() -> std::string;
	auto setMessage(std::string) -> void;
};

class RemoveActorPacket : public Packet {
public:
	RemoveActorPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::RemoveActor).get()->vTable;
	}

	auto getRuntimeID() -> ActorRuntimeID;
};

class MobEffectPacket : public Packet {
public:
	MobEffectPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::MobEffect).get()->vTable;
	}

	auto getRuntimeID() -> ActorRuntimeID;
	auto getEffectID() -> int;
};

class InteractPacket : public Packet {
public:
	enum class Action : unsigned char {
		Invalid = 0,
		StopRiding = 3,
		InteractUpdate = 4,
		NpcOpen = 5,
		OpenInventory = 6,
	};

	InteractPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::Interact).get()->vTable;
	}

	auto getAction() -> Action;
};

class SetActorMotionPacket : public Packet {
public:
	SetActorMotionPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::SetActorMotion).get()->vTable;
	}

	auto getRuntimeID() -> ActorRuntimeID;
	auto getMotion() -> glm::vec3;
	auto setMotion(glm::vec3) -> void;
};

class ContainerOpenPacket : public Packet {
public:
	ContainerOpenPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::ContainerOpen).get()->vTable;
	}

	auto getContainerID() -> ContainerID;
	auto setContainerID(ContainerID containerId) -> void;
	auto getType() -> SharedTypes::Legacy::ContainerType;
	auto setType(SharedTypes::Legacy::ContainerType type) -> void;
	auto setPos(glm::vec3 pos) -> void;
	auto setEntityUniqueID(ActorUniqueID actoruniqueid) -> void;
};

class ContainerClosePacket : public Packet {
public:
	ContainerClosePacket() {
		if (!vTable) vTable = CreatePacket(PacketID::ContainerClose).get()->vTable;
	}

	auto getContainerID() -> ContainerID;
	auto setContainerID(ContainerID containerId) -> void;
	auto setServerInitiatedClose(bool serverInitiatedClose) -> void;
};

class PlayerAuthInputPacket : public Packet {
public:
	enum class InputData : int {
		Ascend = 0,
		Descend = 1,
		NorthJumpDeprecated = 2,
		JumpDown = 3,
		SprintDown = 4,
		ChangeHeight = 5,
		Jumping = 6,
		AutoJumpingInWater = 7,
		Sneaking = 8,
		SneakDown = 9,
		Up = 10,
		Down = 11,
		Left = 12,
		Right = 13,
		UpLeft = 14,
		UpRight = 15,
		WantUp = 16,
		WantDown = 17,
		WantDownSlow = 18,
		WantUpSlow = 19,
		Sprinting = 20,
		AscendBlock = 21,
		DescendBlock = 22,
		SneakToggleDown = 23,
		PersistSneak = 24,
		StartSprinting = 25,
		StopSprinting = 26,
		StartSneaking = 27,
		StopSneaking = 28,
		StartSwimming = 29,
		StopSwimming = 30,
		StartJumping = 31,
		StartGliding = 32,
		StopGliding = 33,
		PerformItemInteraction = 34,
		PerformBlockActions = 35,
		PerformItemStackRequest = 36,
		HandledTeleport = 37,
		Emoting = 38,
		MissedSwing = 39,
		StartCrawling = 40,
		StopCrawling = 41,
		StartFlying = 42,
		StopFlying = 43,
		ClientAckServerData = 44,
		IsInClientPredictedVehicle = 45,
		PaddlingLeft = 46,
		PaddlingRight = 47,
		BlockBreakingDelayEnabled = 48,
		HorizontalCollision = 49,
		VerticalCollision = 50,
		DownLeft = 51,
		DownRight = 52,
		StartUsingItem = 53,
		IsCameraRelativeMovementEnabled = 54,
		IsRotControlledByMoveDirection = 55,
		StartSpinAttack = 56,
		StopSpinAttack = 57,
		IsHotbarOnlyTouch = 58,
		JumpReleasedRaw = 59,
		JumpPressedRaw = 60,
		JumpCurrentRaw = 61,
		SneakReleasedRaw = 62,
		SneakPressedRaw = 63,
		SneakCurrentRaw = 64,
		InputNum = 65,
	};

	PlayerAuthInputPacket() {
		if (!vTable) vTable = CreatePacket(PacketID::PlayerAuthInput).get()->vTable;
	}

	auto getPitch() -> float;
	auto getYaw() -> float;
	auto getPos() -> glm::vec3;
	auto getHeadYaw() -> float;

	auto setPitch(float) -> void;
	auto setYaw(float) -> void;	
	auto setPos(glm::vec3) -> void;
	auto setHeadYaw(float) -> void;

	auto getDeltaPos() -> glm::vec3;
	auto setDeltaPos(glm::vec3) -> void;
	auto setVehicleRot(glm::vec2) -> void;
	auto getMove() -> glm::vec2;
	auto setMove(glm::vec2) -> void;

	auto setInputData(InputData, bool) -> void;
	auto hasInputData(InputData) -> bool;
};
