package com.thebrokenrail.gestus.mixin;

import com.mojang.authlib.GameProfile;
import com.thebrokenrail.gestus.Gestus;
import com.thebrokenrail.gestus.entity.FakePlayerEntity;
import com.thebrokenrail.gestus.util.ServerPlayerEntityExtension;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.EulerAngle;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.Objects;

@Mixin(ServerPlayerEntity.class)
public abstract class MixinServerPlayerEntity extends PlayerEntity implements ServerPlayerEntityExtension {
    @Unique
    private FakePlayerEntity shadow;

    public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile profile) {
        super(world, pos, yaw, profile);
    }

    private void updateShadow() {
        if (shadow != null && (shadow.getEntityWorld() != getEntityWorld() || shadow.removed)) {
            if (!shadow.removed) {
                shadow.remove();
            }
            shadow = null;
        }
        boolean spawn = false;
        if (shadow == null) {
            shadow = new FakePlayerEntity(Gestus.FAKE_PLAYER_ENTITY_TYPE, getEntityWorld(), getDisplayName(), (ServerPlayerEntity) (Object) this);
            spawn = true;
        }

        boolean invisible = isInvisible();

        shadow.setInvisible(invisible);
        shadow.setGlowing(isGlowing());

        shadow.setWorld(getEntityWorld());
        shadow.refreshPositionAndAngles(getX(), getY(), getZ(), yaw, pitch);

        shadow.setHeadRotation(new EulerAngle(pitch, getHeadYaw() - yaw, 0f));

        Vec3d olVVelocity = shadow.getVelocity();
        Vec3d velocity = getVelocity();
        if (!Objects.equals(olVVelocity, velocity)) {
            shadow.setVelocity(velocity);
            shadow.velocityDirty = true;
            shadow.velocityModified = true;
        }

        shadow.setCustomNameVisible(!invisible && !isSneaking());

        shadow.update();

        if (spawn) {
            getEntityWorld().spawnEntity(shadow);
        }
    }

    @Unique
    private Vec3d lastPos = null;

    @Inject(at = @At("HEAD"), method = "tick")
    public void tick(CallbackInfo info) {
        updateShadow();
        lastPos = getPos();
    }

    @Override
    public Vec3d getLastPos() {
        return lastPos;
    }

    @Override
    public FakePlayerEntity getShadow() {
        return shadow;
    }

    @Override
    public void remove() {
        super.remove();
        shadow.remove();
    }

    @Override
    public void swingHand(Hand hand, boolean bl) {
        super.swingHand(hand, bl);
        shadow.hit(hand);
    }
}
