package zenith.zov.base.rotation;

import com.darkmagician6.eventapi.EventManager;
import com.darkmagician6.eventapi.EventTarget;
import java.util.Objects;
import lombok.Generated;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerRotationS2CPacket;
import net.minecraft.util.math.MathHelper;
import zenith.zov.base.events.impl.other.EventSpawnEntity;
import zenith.zov.base.events.impl.player.EventDirection;
import zenith.zov.base.events.impl.player.EventRotate;
import zenith.zov.base.events.impl.player.EventUpdate;
import zenith.zov.base.events.impl.server.EventPacket;
import zenith.zov.base.request.RequestHandler;
import zenith.zov.client.modules.api.Module;
import zenith.zov.utility.game.player.rotation.Rotation;
import zenith.zov.utility.interfaces.IMinecraft;

public class RotationManager implements IMinecraft {
    private Rotation currentRotation = new Rotation(0.0F, 0.0F);
    private Rotation previousRotation = new Rotation(0.0F, 0.0F);
    private final RequestHandler<RotationTarget> requestHandler = new RequestHandler<>();
    private final AimManager aimManager = new AimManager();
    private RotationTarget previousRotationTarget = new RotationTarget(this.currentRotation, () -> this.currentRotation, this.aimManager.getInstantSetup());
    private boolean setRotation = true;

    public RotationManager() {
        EventManager.register(this);
    }

    @EventTarget
    public void addLocalPlayer(EventSpawnEntity eventSpawnLocalPlayer) {
        if (eventSpawnLocalPlayer.getEntity() instanceof ClientPlayerEntity player) {
            this.currentRotation = new Rotation(player.getYaw(), player.getPitch());
            this.previousRotation = new Rotation(player.getYaw(), player.getPitch());
            this.previousRotationTarget = new RotationTarget(this.currentRotation, () -> this.currentRotation, this.aimManager.getInstantSetup());
            this.setRotation = true;
        }
    }

    @EventTarget(3)
    public void update(EventUpdate event) {
        EventManager.call(new EventRotate());
        RotationTarget targetRotation = this.requestHandler.getActiveRequestValue();
        if (targetRotation != null) {
            Rotation newRot = targetRotation.rotation().get();
            this.previousRotation = this.currentRotation;
            this.currentRotation = newRot;
            this.setRotation = false;
            this.previousRotationTarget = targetRotation;
        } else if (this.setRotation) {
            this.previousRotation = this.currentRotation;
            this.currentRotation = this.aimManager
                .rotate(this.aimManager.getInstantSetup(), new Rotation(mc.player.getYaw(), mc.player.getPitch()));
        } else {
            Rotation back = new Rotation(mc.player.getYaw(), mc.player.getPitch());
            if (this.currentRotation.rotationDeltaTo(back).isInRange(5.0F)) {
                this.previousRotation = this.currentRotation;
                this.currentRotation = this.aimManager.rotate(this.aimManager.getInstantSetup(), back);
                this.setRotation = true;
            } else {
                Rotation newRot = this.aimManager.rotate(this.previousRotationTarget.rotationConfigBack(), back);
                this.previousRotation = this.currentRotation;
                this.currentRotation = newRot;
            }
        }

        if (!this.setRotation) {
            float delta = this.currentRotation.getYaw() - mc.player.lastYaw;
            Rotation validing = new Rotation(this.currentRotation.getYaw(), this.currentRotation.getPitch());
            if (delta > 320.0F) {
                validing = new Rotation(mc.player.lastYaw + 300.0F, this.currentRotation.getPitch())
                    .normalize(new Rotation(mc.player.lastYaw, mc.player.lastPitch));
            }

            if (delta < -320.0F) {
                validing = new Rotation(mc.player.lastYaw - 300.0F, this.currentRotation.getPitch())
                    .normalize(new Rotation(mc.player.lastYaw, mc.player.lastPitch));
            }

            this.currentRotation = validing;
        }

        this.currentRotation = new Rotation(this.currentRotation.getYaw(), MathHelper.clamp(this.currentRotation.getPitch(), -90.0F, 90.0F));
        this.requestHandler.tick();
    }

    public void setRotation(RotationTarget targetRotation, int priority, Module module) {
        this.requestHandler.request(new RequestHandler.Request<>(2, priority, module, targetRotation));
    }

    @EventTarget
    public void direction(EventDirection direction) {
        direction.setYaw(this.currentRotation.getYaw());
        direction.setPitch(this.currentRotation.getPitch());
    }

    @EventTarget
    public void packet(EventPacket eventPacket) {
        Packet var10000 = eventPacket.getPacket();
        Objects.requireNonNull(var10000);
        Object var2 = var10000;
        switch (var2) {
            case PlayerRotationS2CPacket player:
                this.currentRotation = new Rotation(player.xRot(), player.yRot());
                this.previousRotationTarget = new RotationTarget(this.currentRotation, () -> this.currentRotation, this.aimManager.getInstantSetup());
                this.setRotation = true;
                break;
            case PlayerPositionLookS2CPacket playerx:
                this.currentRotation = new Rotation(playerx.change().yaw(), playerx.change().pitch());
                this.previousRotationTarget = new RotationTarget(this.currentRotation, () -> this.currentRotation, this.aimManager.getInstantSetup());
                this.setRotation = true;
                break;
            case PlayerInteractItemC2SPacket packetItem:
                packetItem.yaw = this.currentRotation.getYaw();
                packetItem.pitch = this.currentRotation.getPitch();
                break;
        }
    }

    @Generated
    public Rotation getCurrentRotation() {
        return this.currentRotation;
    }

    @Generated
    public Rotation getPreviousRotation() {
        return this.previousRotation;
    }

    @Generated
    public RequestHandler<RotationTarget> getRequestHandler() {
        return this.requestHandler;
    }

    @Generated
    public AimManager getAimManager() {
        return this.aimManager;
    }

    @Generated
    public RotationTarget getPreviousRotationTarget() {
        return this.previousRotationTarget;
    }

    @Generated
    public boolean isSetRotation() {
        return this.setRotation;
    }
}
