package zenith.zov.utility.mixin.client;

import com.darkmagician6.eventapi.EventManager;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.Camera;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.BlockPos.Mutable;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import zenith.zov.base.events.impl.render.EventCamera;
import zenith.zov.base.events.impl.render.EventCameraPosition;
import zenith.zov.utility.game.player.rotation.Rotation;

@Mixin({Camera.class})
public abstract class CameraMixin {
    @Shadow
    private Vec3d pos;
    @Shadow
    @Final
    private Mutable blockPos;
    @Shadow
    private float yaw;
    @Shadow
    private float pitch;

    @Shadow
    protected abstract void setRotation(float var1, float var2);

    @Shadow
    protected abstract void moveBy(float var1, float var2, float var3);

    @Shadow
    protected abstract float clipToSpace(float var1);

    @Inject(
        method = {"update"},
        at = {@At(
            value = "INVOKE",
            target = "Lnet/minecraft/client/render/Camera;setPos(DDD)V",
            shift = Shift.AFTER
        )},
        cancellable = true
    )
    private void updateHook(BlockView area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickDelta, CallbackInfo ci) {
        EventCamera event = new EventCamera(false, 4.0F, new Rotation(this.yaw, this.pitch));
        EventManager.call(event);
        Rotation angle = event.getAngle();
        if (event.isCancelled() && focusedEntity instanceof ClientPlayerEntity player && !player.isSleeping() && thirdPerson) {
            float pitch = inverseView ? -angle.getPitch() : angle.getPitch();
            float yaw = angle.getYaw() - (float)(inverseView ? 180 : 0);
            float distance = event.getDistance();
            this.setRotation(yaw, pitch);
            this.moveBy(event.isCameraClip() ? -distance : -this.clipToSpace(distance), 0.0F, 0.0F);
            ci.cancel();
        }
    }

    @Inject(
        method = {"setPos(Lnet/minecraft/util/math/Vec3d;)V"},
        at = {@At("HEAD")},
        cancellable = true
    )
    private void posHook(Vec3d pos, CallbackInfo ci) {
        EventCameraPosition event = new EventCameraPosition(pos);
        EventManager.call(event);
        this.pos = pos = event.getPos();
        this.blockPos.set(pos.x, pos.y, pos.z);
        ci.cancel();
    }
}
