#include "AntiRegen.h"
#include "Client.h"

#include "Utils/MoveUtils.h"
#include "Utils/TimeUtils.h"

auto AntiRegen::canPlace(glm::vec3 pos) -> bool {
	glm::vec3 floorpos = glm::vec3(
		std::floor(pos.x),
		std::floor(pos.y),
		std::floor(pos.z)
	);
	glm::ivec3 ifloorpos = glm::ivec3(
		floorpos.x,
		floorpos.y,
		floorpos.z
	);
	return Client::clientinstance->getBlockSource()->getBlock(&ifloorpos)->getBlockLegacy()->getName() == _("tile.air");
}

auto AntiRegen::buildBlock(glm::vec3 pos, GameMode* gamemode, Player* localplayer, PlayerInventory* playerinventory, Inventory* inventory) -> bool {
	static std::vector<glm::vec3> neighbours = {
		glm::vec3(0, -1, 0),
		glm::vec3(0, 1, 0),
		glm::vec3(0, 0, -1),
		glm::vec3(0, 0, 1),
		glm::vec3(-1, 0, 0),
		glm::vec3(1, 0, 0)
	};

	for (int face = 0; face < neighbours.size(); face++) {
		glm::vec3 floorpos = glm::vec3(
			std::floor(pos.x),
			std::floor(pos.y),
			std::floor(pos.z)
		);

		glm::vec3 offset = glm::vec3(
			floorpos.x - neighbours[face].x,
			floorpos.y - neighbours[face].y,
			floorpos.z - neighbours[face].z
		);
		if (!canPlace(offset)) {
			glm::ivec3 ioffset = glm::ivec3(
				(int)offset.x,
				(int)offset.y,
				(int)offset.z
			);

			if (autoblock) {
				int counts = 0;
				int slot = -1;
				for (int i = 0; i < 9; i++) {
					auto itemstack = inventory->getItemStack(i);
					if (itemstack->getItem() && itemstack->getBlock() && !((*itemstack->getItem())->getName().find(_("boombox")) != std::string::npos)) {
						if (counts < itemstack->getCount()) {
							counts = itemstack->getCount();
							slot = i;
						}
					}
				}

				if (slot != -1) {
					playerinventory->setSelectedSlot(slot);
				}
			}

			localplayer->swing();

			long long now = Utils::Time::getMs();

			{
				static long long start = now;
				if (minplacedelay != 0.f) {
					static float currentdelay = Utils::Math::getrandom(minplacedelay * 10.f, maxplacedelay * 10.f);

					if (now - start >= currentdelay / 1.5f) {
						placing = true;
						blockpos = ioffset;
					}
					if (now - start >= currentdelay) {
						gamemode->buildBlock(ioffset, face, false);
						currentdelay = Utils::Math::getrandom(minplacedelay * 10.f, maxplacedelay * 10.f);
						placing = false;
						start = now;
					}
				}
				else {
					gamemode->buildBlock(ioffset, face, false);
					start = now;
				}
			}

			Client::swingspeed = -1;

			return true;
		}
	}

	return false;
}

auto AntiRegen::onWorldTick(Player* _this) -> void {

	auto clientinstance = Client::clientinstance;
	if (!clientinstance) return;

	auto blocksource = clientinstance->getBlockSource();
	if (!blocksource) return;

	auto gamemode = _this->getGameMode();
	if (!gamemode) return;

	auto playerinventory = _this->getPlayerInventory();
	if (!playerinventory) return;

	auto inventory = playerinventory->getInventory();
	if (!inventory) return;

	std::vector<glm::ivec3> placelists;

	glm::vec3 position = Utils::Move::getBlockPosition(_this);


	bool hasBlocks = false;
	for (int i = 0; i < 9; i++) {
		auto itemstack = inventory->getItemStack(i);
		if (itemstack->getItem() && itemstack->getBlock() && !((*itemstack->getItem())->getName().find(_("boombox")) != std::string::npos)) {
			hasBlocks = true;
			break;
		}
	}
	if (!hasBlocks) return;

	for (int x = -static_cast<int>(range); x < static_cast<int>(range); x++) {
		for (int y = -static_cast<int>(range); y < static_cast<int>(range); y++) {
			for (int z = -static_cast<int>(range); z < static_cast<int>(range); z++) {
				glm::ivec3 blockpos = glm::ivec3(position.x + x, position.y + y, position.z + z);
				std::string name = blocksource->getBlock(&blockpos)->getBlockLegacy()->getName();
				if (name == _("tile.redstone_ore") || name == _("tile.lit_redstone_ore")) {
					std::vector<glm::ivec3> checks = {
						glm::ivec3(0, 1, 0),
						//glm::ivec3(0, -1, 0),
						glm::ivec3(1, 0, 0),
						glm::ivec3(-1, 0, 0),
						glm::ivec3(0, 0, 1),
						glm::ivec3(0, 0, -1)
					};

					bool done = false;
					for (glm::ivec3 check : checks) {
						glm::ivec3 addedcheck = glm::ivec3(blockpos.x + check.x, blockpos.y + check.y, blockpos.z + check.z);
						if (blocksource->getBlock(&addedcheck)->getBlockLegacy()->getName() == _("tile.air")) {
							placelists.push_back(addedcheck);
							done = true;
							break;
						}
					}
					if (done) {
						continue;
					}
				}
			}
		}
	}

	for (glm::ivec3 pos : placelists) {
		if (canPlace(pos)) {
			if (buildBlock(pos, gamemode, _this, playerinventory, inventory)) {
				break;
			}
		}
	}
}

auto AntiRegen::handlePacket(Packet* packet, Player* arg1) -> void {
	auto localplayer = arg1;
	if (!localplayer) return;
	if (!placing) return;

	if (packet->getID() == PacketID::PlayerAuthInput) {
		PlayerAuthInputPacket* playerauthinput = reinterpret_cast<PlayerAuthInputPacket*>(packet);

		glm::vec2 angle = Utils::Math::getAngle(Utils::Move::getEyePosition(localplayer), blockpos);
		switch (rotations) {
		case 0:
		case 1:
			playerauthinput->setPitch(angle.x);
			playerauthinput->setYaw(angle.y);
			playerauthinput->setHeadYaw(angle.y);
			break;
		}
	}
}

auto AntiRegen::onPacketSendBefore(Packet* packet) -> void {
	if (!placing) return;
	if (rotations == 2) return;

	auto clientinstance = Client::clientinstance;
	if (!clientinstance) return;

	auto localplayer = clientinstance->getLocalPlayer();
	if (!localplayer) return;

	auto actorrotationcomponent = localplayer->getActorRotationComponent();
	if (!actorrotationcomponent) return;

	handlePacket(packet, localplayer);
}

auto AntiRegen::onPacketSendAfter2(Packet* packet, bool* cancel) -> void {
	if (!placing) return;
	if (rotations != 2) return;

	auto clientinstance = Client::clientinstance;
	if (!clientinstance) return;

	auto localplayer = clientinstance->getLocalPlayer();
	if (!localplayer) return;

	auto actorrotationcomponent = localplayer->getActorRotationComponent();
	if (!actorrotationcomponent) return;

	handlePacket(packet, localplayer);
}

auto AntiRegen::onDisabled() -> void {
	placing = false;
}
