package me.deftware.mixin.mixins;

import me.deftware.client.framework.event.events.*;
import me.deftware.client.framework.maps.SettingsMap;
import me.deftware.client.framework.math.vector.Vector3d;
import me.deftware.client.framework.render.camera.entity.CameraEntityMan;
import me.deftware.mixin.imp.IMixinEntity;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityPose;
import net.minecraft.util.math.Vec3d;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@SuppressWarnings("ConstantConditions")
@Mixin(Entity.class)
public abstract class MixinEntity implements IMixinEntity {

    @Shadow
    public boolean noClip;

    @Shadow
    protected boolean onGround;

    @Shadow
    public float pitch;

    @Shadow
    public float yaw;

    @Shadow
    protected boolean inNetherPortal;

    @Shadow
    public abstract boolean isSneaking();

    @Shadow
    public abstract boolean isSprinting();

    @Shadow
    protected abstract boolean getFlag(int id);

    @Shadow
    protected Vec3d movementMultiplier;

    @SuppressWarnings("ConstantConditions")
    @Inject(method = "changeLookDirection", at = @At("HEAD"), cancellable = true)
    public void changeLookDirection(double cursorX, double cursorY, CallbackInfo ci) {
        if ((Object) this == MinecraftClient.getInstance().player && CameraEntityMan.isActive()) {
            CameraEntityMan.fakePlayer.changeLookDirection(cursorX, cursorY);
            CameraEntityMan.fakePlayer.setHeadYaw(CameraEntityMan.fakePlayer.yaw);
            ci.cancel();
        }
    }

    @Redirect(method = "updateMovementInFluid(Lnet/minecraft/tag/Tag;D)Z", at = @At(value = "INVOKE",
            target = "Lnet/minecraft/entity/Entity;setVelocity(Lnet/minecraft/util/math/Vec3d;)V",
            opcode = 182))
    private void applyFluidVelocity(Entity entity, Vec3d velocity) {
        EventFluidVelocity event = new EventFluidVelocity(new Vector3d(velocity));
        if (!event.isCanceled()) {
            entity.setVelocity(event.getVector3d().getMinecraftVector());
        }
    }

    @Inject(method = "pushAwayFrom", at = @At("HEAD"), cancellable = true)
    public void pushAwayFrom(Entity entity, CallbackInfo info) {
        if (((Object) this) == MinecraftClient.getInstance().player) {
            if (new EventEntityPush(me.deftware.client.framework.entity.Entity.newInstance(entity)).broadcast().isCanceled()) {
                info.cancel();
            }
        }
    }

    @Inject(method = "getPose", at = @At(value = "TAIL"), cancellable = true)
    private void onGetPose(CallbackInfoReturnable<EntityPose> cir) {
        if ((boolean) SettingsMap.getValue(SettingsMap.MapKeys.ENTITY_SETTINGS, "SWIMMING_MODE_OVERRIDE", false)) {
            cir.setReturnValue(EntityPose.SWIMMING);
        }
    }

    @Redirect(method = "move", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/Entity;noClip:Z", opcode = 180))
    private boolean noClipCheck(Entity self) {
        boolean noClipCheck = (boolean) SettingsMap.getValue(SettingsMap.MapKeys.ENTITY_SETTINGS, "NOCLIP", false);
        return noClip || noClipCheck && self instanceof ClientPlayerEntity;
    }

    @Inject(method = "slowMovement", at = @At(value = "TAIL"), cancellable = true)
    private void onSlowMovement(BlockState state, Vec3d multiplier, CallbackInfo ci) {
        EventSlowdown event = new EventSlowdown(EventSlowdown.SlowdownType.Web);
        event.broadcast();
        if (event.isCanceled()) {
            Vec3d cobSlowness = new Vec3d(0.25D, 0.05000000074505806D, 0.25D);
            if (multiplier.x == cobSlowness.x && multiplier.y == cobSlowness.y && multiplier.z == cobSlowness.z) {
                this.movementMultiplier = Vec3d.ZERO;
                ci.cancel();
            }
        }
    }

    @Redirect(method = "move", at = @At(value = "INVOKE", target = "net/minecraft/entity/Entity.isSneaking()Z", opcode = 180, ordinal = 0))
    private boolean sneakingCheck(Entity self) {
        EventSneakingCheck event = new EventSneakingCheck(isSneaking());
        event.broadcast();
        if (event.isSneaking()) {
            return true;
        }
        return getFlag(1);
    }

    @Inject(method = "setVelocityClient", at = @At("HEAD"), cancellable = true)
    private void onSetVelocityClient(double x, double y, double z, CallbackInfo ci) {
        if ((Object) this == MinecraftClient.getInstance().player) {
            EventKnockback event = new EventKnockback(x, y, z);
            event.broadcast();
            if (event.isCanceled()) {
                ci.cancel();
            }
        }
    }

    @Override
    public boolean getAFlag(int id) {
        return getFlag(id);
    }

    @Override
    public void setInPortal(boolean inPortal) {
        this.inNetherPortal = inPortal;
    }

}

@@ -2315,24 +2315,90 @@
 velocity) %7B%0A
+        if (entity == MinecraftClient.getInstance().player) %7B%0A    
         Even
@@ -2472,24 +2472,28 @@
 ));%0A        
+    
 if (!event.i
@@ -2499,32 +2499,36 @@
 isCanceled()) %7B%0A
+    
             enti
@@ -2581,24 +2581,38 @@
 tVector());%0A
+            %7D%0A
         %7D%0A  
@@ -3160,24 +3160,99 @@
 ose%3E cir) %7B%0A
+        if (((Object) this) == MinecraftClient.getInstance().player) %7B%0A    
         if (
@@ -3367,16 +3367,20 @@
         
+    
 cir.setR
@@ -3412,16 +3412,30 @@
 MMING);%0A
+            %7D%0A
         
@@ -3973,32 +3973,107 @@
 lbackInfo ci) %7B%0A
+        if (((Object) this) == MinecraftClient.getInstance().player) %7B%0A    
         EventSlo
@@ -4137,32 +4137,36 @@
 e.Web);%0A        
+    
 event.broadcast(
@@ -4168,32 +4168,36 @@
 cast();%0A        
+  
+  
 if (event.isCanc
@@ -4198,32 +4198,36 @@
 isCanceled()) %7B%0A
+    
             Vec3
@@ -4297,24 +4297,28 @@
             
+ 
+   
 if (multipli
@@ -4420,24 +4420,28 @@
             
+    
 this.movemen
@@ -4474,32 +4474,36 @@
                 
+    
 ci.cancel();%0A   
@@ -4491,32 +4491,50 @@
    ci.cancel();%0A
+                %7D%0A
             %7D%0A  

Apply the same fix to all but sneakingCheck