package com.thebrokenrail.herobrine.entity.ai.stage;

import com.thebrokenrail.herobrine.config.HardcodedConfig;
import com.thebrokenrail.herobrine.entity.HerobrineEntity;
import com.thebrokenrail.herobrine.entity.ai.AIStage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;

public class IdleStage extends AIStage {
    private int time = 0;

    public IdleStage(HerobrineEntity entity) {
        super(entity);
    }

    @Override
    public void tick() {
        if (time < HardcodedConfig.HEROBRINE_MINIMUM_IDLE_TIME) {
            time++;
        } else if (getEntity().getRandom().nextDouble() < HardcodedConfig.HEROBRINE_IDLE_END_CHANCE) {
            nextStage();
            return;
        }

        ServerPlayerEntity nearest = getEntity().getNearestPlayer();

        boolean moving = false;
        if (nearest != null) {
            float maxDiff = Math.abs(target(getTargetYaw(nearest.getPos()), getTargetPitch(nearest.getPos())));

            if (maxDiff < HardcodedConfig.HEROBRINE_FOLLOW_ANGLE_THRESHOLD) {
                Vec3d diff = getEntity().getRotationVec(1.0f);
                diff = diff.multiply(HardcodedConfig.HEROBRINE_FLY_SPEED);

                float distance = Math.abs(getEntity().distanceTo(nearest));
                boolean go = false;
                if (distance > (HardcodedConfig.HEROBRINE_RECOMMENDED_DISTANCE_CENTER + HardcodedConfig.HEROBRINE_RECOMMENDED_DISTANCE_DIFF)) {
                    go = true;
                } else if (distance < (HardcodedConfig.HEROBRINE_RECOMMENDED_DISTANCE_CENTER - HardcodedConfig.HEROBRINE_RECOMMENDED_DISTANCE_DIFF)) {
                    go = true;
                    // Prefer Moving On X And Z When Backing Away
                    diff = new Vec3d(diff.getX(), 0, diff.getZ()).multiply(-1);
                }
                if (go) {
                    getEntity().addVelocity(diff.getX(), diff.getY(), diff.getZ());
                    moving = true;
                }
            }
        }
        if (!moving) {
            getEntity().setVelocity(getEntity().getVelocity().multiply(0.25d));
            getEntity().velocityDirty = true;
        }
    }

    private Vec3d getPosVec() {
        return getEntity().getPos();
    }

    private static final double RAD2DEG = 180d / Math.PI;

    private float getTargetPitch(Vec3d targetPos) {
        Vec3d pos = getPosVec();
        double diffX = targetPos.getX() - pos.getX();
        double diffY = targetPos.getY() - pos.getY();
        double diffZ = targetPos.getZ() - pos.getZ();
        double g = MathHelper.sqrt(diffX * diffX + diffZ * diffZ);
        return (float) -(MathHelper.atan2(diffY, g) * RAD2DEG);
    }

    private float getTargetYaw(Vec3d targetPos) {
        Vec3d pos = getPosVec();
        double diffX = targetPos.getX() - pos.getX();
        double diffZ = targetPos.getZ() - pos.getZ();
        return (float) (MathHelper.atan2(diffZ, diffX) * RAD2DEG) - 90f;
    }

    private static final float ROTATION_INCREMENT = 4;

    private void change(float diffYaw, float diffPitch) {
        getEntity().yaw = diffYaw;
        getEntity().headYaw = diffYaw;
        getEntity().pitch = diffPitch;
    }

    private float changeAngle(float from, float to, float max) {
        float f = MathHelper.subtractAngles(from, to);
        float g = MathHelper.clamp(f, -max, max);
        return from + g;
    }

    private float[] getAngleChange(float from, float to) {
        float diff = MathHelper.subtractAngles(from, to);
        float diff2 = changeAngle(from, to, ROTATION_INCREMENT);
        return new float[]{diff2, diff};
    }

    private float target(float targetYaw, float targetPitch) {
        float[] yawChange = getAngleChange(getEntity().headYaw, targetYaw);
        float[] pitchChange = getAngleChange(getEntity().pitch, targetPitch);
        change(yawChange[0], pitchChange[0]);
        return Math.max(yawChange[1], pitchChange[1]);
    }

    @Override
    public void fromTag(CompoundTag tag) {
        time = tag.getInt("Time");
    }

    @Override
    public CompoundTag toTag() {
        CompoundTag tag = new CompoundTag();
        tag.putInt("Time", time);
        return tag;
    }
}
