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

#include "Mod/Speed.h"
#include "Mod/Fly.h"

auto Disabler::onRunUpdateCycle(bool* cancel) -> void {
	if (cancelrunupdatecycle)
		*cancel = true;
}

auto Disabler::onPacketSendAfter3(Packet* packet, bool* cancel) -> void {
	auto clientinstance = Client::clientinstance;
	if (!clientinstance) return;

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

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

	switch (mode) {
	case 0:
		if (packet->getID() == PacketID::PlayerAuthInput) {
			auto playerauthinput = reinterpret_cast<PlayerAuthInputPacket*>(packet);

            glm::vec2 moveVec = playerauthinput->getMove();
            glm::vec2 xzVel;
            {
                float yaw = localplayer->getActorRotationComponent()->mRotationDegree.y;
                float outYawOffset = 0.f;
                {
                    if (Utils::Move::isMoving()) {
                        bool w = Utils::Move::isWalking(localplayer);
                        bool s = Utils::Move::isBacking(localplayer);
                        bool a = Utils::Move::isLefting(localplayer);
                        bool d = Utils::Move::isRighting(localplayer);

                        float yawOffset = 0;
                        if (w && a)
                            yawOffset = -45;
                        else if (w && d)
                            yawOffset = 45;
                        else if (s && a)
                            yawOffset = -135;
                        else if (s && d)
                            yawOffset = 135;
                        else if (w)
                            yawOffset = 0;
                        else if (a)
                            yawOffset = -90;
                        else if (s)
                            yawOffset = -180;
                        else if (d)
                            yawOffset = 90;
                        else
                            yawOffset = 0;
                        outYawOffset = yawOffset;
                    }

                }
                yaw += outYawOffset + 90;

                float calcYaw = glm::radians(yaw);

                glm::vec2 motion;
                motion.x = cos(calcYaw) * 1.f;
                motion.y = sin(calcYaw) * 1.f;

                xzVel = motion;
            }

            float yaw = playerauthinput->getYaw();
            yaw = -yaw;

            if (moveVec.x == 0 && moveVec.y == 0 && xzVel.x == 0 && xzVel.y == 0) return;

            float moveVecYaw = atan2(moveVec.x, moveVec.y);
            moveVecYaw = glm::degrees(moveVecYaw);

            float movementYaw = atan2(xzVel.x, xzVel.y);
            float movementYawDegrees = movementYaw * (180.0f / 3.14159265358979323846f);

            float yawDiff = (roundf(movementYawDegrees / 45.f) * 45) - (roundf(yaw / 45.f) * 45);

            float newMoveVecX = sin(glm::radians(yawDiff));
            float newMoveVecY = cos(glm::radians(yawDiff));
            glm::vec2 newMoveVec = { newMoveVecX, newMoveVecY };

            if (abs(newMoveVec.x) < 0.001) newMoveVec.x = 0;
            if (abs(newMoveVec.y) < 0.001) newMoveVec.y = 0;
            if (moveVec.x == 0 && moveVec.y == 0) newMoveVec = { 0, 0 };

            static bool isSprinting = false;
            bool startedThisTick = false;

            if (playerauthinput->hasInputData(PlayerAuthInputPacket::InputData::StartSprinting)) {
                isSprinting = true;
                startedThisTick = true;
            }
            else if (playerauthinput->hasInputData(PlayerAuthInputPacket::InputData::StopSprinting)) {
                isSprinting = false;
            }

            playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Up, false);
            playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Down, false);
            playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Left, false);
            playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Right, false);
            
            bool forward = newMoveVec.y > 0;
            bool backward = newMoveVec.y < 0;
            bool left = newMoveVec.x > 0;
            bool right = newMoveVec.x < 0;
            if (!forward && (backward || left || right) && isSprinting) {
                static bool sprint = false;
                if (!Utils::Move::isOnGround(localplayer)/* && !player->wasOnGround()*/) sprint = false;
                if (sprint) {
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::SprintDown, true);
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Sprinting, true);
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StartSprinting, true);
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StopSprinting, false);
                }
                else {
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StopSprinting, true);

                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::SprintDown, false);
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Sprinting, false);
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StartSprinting, false);
                }
                sprint = !sprint;
            }
            else if (forward && isSprinting) {
                playerauthinput->setInputData(PlayerAuthInputPacket::InputData::SprintDown, true);
                playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Sprinting, true);
                playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StartSprinting, true);
                playerauthinput->setInputData(PlayerAuthInputPacket::InputData::StopSprinting, false);
            }
            if (forward) playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Up, true);
            if (backward) playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Down, true);
            if (left) playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Left, true);
            if (right) playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Right, true);

            playerauthinput->setMove(newMoveVec);
            playerauthinput->setVehicleRot(newMoveVec);
        }
		break;
	case 1:
		if (packet->getID() == PacketID::PlayerAuthInput) {
			auto playerauthinput = reinterpret_cast<PlayerAuthInputPacket*>(packet);

			long long now = Utils::Time::getMs();
			static long long start = now;
			static bool pcancel = false;
			if (now - start >= bps * 10.f) {
				pcancel = !pcancel;
				cancelrunupdatecycle = !cancelrunupdatecycle;

				start = now;
			}
			auto speedMod = Client::findMod<Speed>(_("Speed"));
			auto flyMod = Client::findMod<Fly>(_("Fly"));

			if (speedMod->enabled || flyMod->enabled)
				*cancel = pcancel;

            if (sendinput) {
                if (flyMod->enabled)
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Up, true);
                else
                    playerauthinput->setInputData(PlayerAuthInputPacket::InputData::Up, false);
            }
		}
		break;
	}
}

auto Disabler::onClient() -> void {
	for (Setting* setting : Client::findSetting(this, _("Blink Amount"))) {
		setting->visible = mode == 1;
	}
}

auto Disabler::getMode() -> std::string {
	switch (mode) {
	case 0:
		return _("Flareon");
	case 1:
		return _("Sentinel");
	}

	return _("Unknown");
}
