#include "Breaker.h"
#include "Client.h"

#include "Utils/MoveUtils.h"
#include "Utils/TimeUtils.h"
#include "Utils/ChatUtils.h"
#include "Utils/ImGuiRenderUtils.h"
#include "Utils/ColorUtils.h"
#include "Utils/TimeUtils.h"

#include "Mod/Teams.h"

auto Breaker::onBaseTick(Player* _this) -> void {
	auto clientinstance = Client::clientinstance;
	if (!clientinstance) return;

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

	if (_this != localplayer) return;

	auto level = _this->getLevel();
	if (!level) return;

	if (!enabled) return;

	std::vector<Actor*> list;
	for (Actor* actor : level->getRuntimeActorList()) {
		if (actor && actor != _this && actor->getActorTypeComponent()->mType == ActorType::Mob) {
			if (Utils::Math::getDistance(Utils::Move::getPosition(_this), Utils::Move::getPositionEntity(actor)) < 6.f) {
				if (actor->getEntityIdentifier() == _("hivesky:death_crate"))
					list.push_back(actor);
			}
		}
	}
	targets = list;
}

auto Breaker::onWorldTick(Player* _this) -> void {
	long long now = Utils::Time::getMs();

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

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

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

	if (startfoundp2) {
		start = now;

		if (now - delstart >= 1000.f) {
			startfoundp1 = false;
			startfoundp2 = false;

			std::vector<glm::ivec3> beds;
			for (int x = -60; x < 60; x++) {
				for (int y = -20; y < 20; y++) {
					for (int z = -60; z < 60; 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.hive:bed_hitbox")) {
							beds.push_back(blockpos);

						}
					}
				}
			}

			if (!beds.empty()) {
				Utils::Chat::display_(_("Founded near beds"), Utils::Chat::Type::GREEN);

				glm::vec3 currentbed;
				float mindistance = 60.f * 2.f;
				for (glm::ivec3 bed : beds) {
					float currentdistance = Utils::Math::getDistance(bed, position);
					if (currentdistance < mindistance) {
						currentbed = bed;
						mindistance = currentdistance;
					}
				}
				teambed = currentbed;
				
				return;
			}


			Utils::Chat::display_(_("Not found bed"), Utils::Chat::Type::RED);
		}

		return;
	}
	if (!enabled) return;

	auto gamemode = _this->getGameMode();
	if (!gamemode) return;
	
	if (lootbox) {
		if (!targets.empty()) {
			for (Actor* target : targets) {
				gamemode->attack(*target);
			}
		}
	}

	static bool startdestroy = false;
	if (pos.x != glm::vec3().x) {
		static bool destroyed = false;
		static int currentface = -2;
		if (Utils::Math::getDistance(pos, position) > 6.f) {
			if (currentface != -2)
				gamemode->stopDestroyBlock(pos, currentface);
			currentface = -2;
			destroyed = false;
			startdestroy = false;
			pos = glm::ivec3();
			return;
		}
		if (currentface == -2) {
			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
				);

				glm::vec3 floorpos2 = glm::vec3(
					std::floor(offset.x),
					std::floor(offset.y),
					std::floor(offset.z)
				);
				glm::ivec3 ifloorpos2 = glm::ivec3(
					floorpos.x,
					floorpos.y,
					floorpos.z
				);

				if (blocksource->getBlock(&ifloorpos2)->getBlockLegacy()->getName() != _("tile.air")) {
					currentface = face;
				}
			}
		}

		if (!startdestroy) {
			start = now;
			destroyed = false;

			gamemode->startDestroyBlock(pos, currentface, destroyed);
			startdestroy = true;
		}
		else {
			if (now - start >= delay * 1000.f) {
				gamemode->destroyBlock(pos, currentface);

				startdestroy = false;
				pos = glm::vec3();
				destroyed = true;
				currentface = -2;
				
				return;
			}
		}

		return;
	}

	for (int x = -6; x < 6; x++) {
		for (int y = -6; y < 6; y++) {
			for (int z = -6; z < 6; 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.hive:bed_hitbox") || name == _("tile.bed")) && bed) {
					if (name == _("tile.hive:bed_hitbox")) {
						bool hasRange = false;
						if (Client::findMod<Teams>(_("Teams"))->enabled) {
							for (int ix = -2; ix < 2; ix++) {
								for (int iz = -2; iz < 2; iz++) {
									glm::ivec3 iblockpos = glm::ivec3(blockpos.x + ix, blockpos.y, blockpos.z + iz);

									if (iblockpos == teambed)
										hasRange = true;
								}
							}

						}

						if (!hasRange) {
							pos = blockpos;
							founded = true;
							return;
						}
					}
					else {
						pos = blockpos;
						founded = true;
						return;
					}
				}
			}
		}
	}

	start = now;
}

auto Breaker::onPacketSendBefore(Packet* packet) -> void {
	if (!enabled) return;

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

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

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

	static glm::ivec3 lastpos = glm::ivec3();


	if (!doRot) {
		if (now - start >= delay * 600.f) {
			rotstart = now;
			lastpos = pos;
			doRot = true;
		}
	}
	else {
		if (now - rotstart >= delay * 500.f) {
			doRot = false;
		}
	}

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


			glm::vec2 angle = Utils::Math::getAngle(Utils::Move::getPosition(localplayer), lastpos);

			playerauthinput->setPitch(angle.x);
			playerauthinput->setYaw(angle.y);
			playerauthinput->setHeadYaw(angle.y);
		}
	}
}

auto Breaker::onPacketReceive(Packet* packet, bool* cancel) -> void {
	if (startfoundp1) return;

	if (packet->getID() == PacketID::Text) {
		auto text = reinterpret_cast<TextPacket*>(packet);
		if (text->getMessage() == reinterpret_cast<const char*>(_(u8"§b§l» §r§a§lVoting has ended!"))) {
			Utils::Chat::display_(_("Starting find your bed..."));
			startfoundp1 = true;
		}
	}
}

auto Breaker::onDimensionChanged() -> void {
	if (!startfoundp1) return;

	startfoundp2 = true;
	Utils::Chat::display_(_("Finding your bed..."));
	delstart = Utils::Time::getMs();
}

auto Breaker::getMode() -> std::string {
	return _("Normal");
}
