package net.minecraft.world.entity.monster;

import net.minecraft.world.entity.Entity;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.Position;
import java.util.EnumSet;
import net.minecraft.world.level.LightLayer;
import java.util.Random;
import net.minecraft.world.entity.raid.Raid;
import net.minecraft.world.entity.EquipmentSlot;
import javax.annotation.Nullable;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.core.BlockPos;

public abstract class PatrollingMonster extends Monster {
    private BlockPos patrolTarget;
    private boolean patrolLeader;
    private boolean patrolling;
    
    protected PatrollingMonster(final EntityType<? extends PatrollingMonster> aoq, final Level bqb) {
        super(aoq, bqb);
    }
    
    @Override
    protected void registerGoals() {
        super.registerGoals();
        this.goalSelector.addGoal(4, new LongDistancePatrolGoal<>(this, 0.7, 0.595));
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag le) {
        super.addAdditionalSaveData(le);
        if (this.patrolTarget != null) {
            le.put("PatrolTarget", NbtUtils.writeBlockPos(this.patrolTarget));
        }
        le.putBoolean("PatrolLeader", this.patrolLeader);
        le.putBoolean("Patrolling", this.patrolling);
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag le) {
        super.readAdditionalSaveData(le);
        if (le.contains("PatrolTarget")) {
            this.patrolTarget = NbtUtils.readBlockPos(le.getCompound("PatrolTarget"));
        }
        this.patrolLeader = le.getBoolean("PatrolLeader");
        this.patrolling = le.getBoolean("Patrolling");
    }
    
    @Override
    public double getMyRidingOffset() {
        return -0.45;
    }
    
    public boolean canBeLeader() {
        return true;
    }
    
    @Nullable
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bqc, final DifficultyInstance ane, final MobSpawnType apb, @Nullable final SpawnGroupData apo, @Nullable final CompoundTag le) {
        if (apb != MobSpawnType.PATROL && apb != MobSpawnType.EVENT && apb != MobSpawnType.STRUCTURE && this.random.nextFloat() < 0.06f && this.canBeLeader()) {
            this.patrolLeader = true;
        }
        if (this.isPatrolLeader()) {
            this.setItemSlot(EquipmentSlot.HEAD, Raid.getLeaderBannerInstance());
            this.setDropChance(EquipmentSlot.HEAD, 2.0f);
        }
        if (apb == MobSpawnType.PATROL) {
            this.patrolling = true;
        }
        return super.finalizeSpawn(bqc, ane, apb, apo, le);
    }
    
    public static boolean checkPatrollingMonsterSpawnRules(final EntityType<? extends PatrollingMonster> aoq, final LevelAccessor bqc, final MobSpawnType apb, final BlockPos fu, final Random random) {
        return bqc.getBrightness(LightLayer.BLOCK, fu) <= 8 && Monster.checkAnyLightMonsterSpawnRules(aoq, bqc, apb, fu, random);
    }
    
    @Override
    public boolean removeWhenFarAway(final double double1) {
        return !this.patrolling || double1 > 16384.0;
    }
    
    public void setPatrolTarget(final BlockPos fu) {
        this.patrolTarget = fu;
        this.patrolling = true;
    }
    
    public BlockPos getPatrolTarget() {
        return this.patrolTarget;
    }
    
    public boolean hasPatrolTarget() {
        return this.patrolTarget != null;
    }
    
    public void setPatrolLeader(final boolean boolean1) {
        this.patrolLeader = boolean1;
        this.patrolling = true;
    }
    
    public boolean isPatrolLeader() {
        return this.patrolLeader;
    }
    
    public boolean canJoinPatrol() {
        return true;
    }
    
    public void findPatrolTarget() {
        this.patrolTarget = this.blockPosition().offset(-500 + this.random.nextInt(1000), 0, -500 + this.random.nextInt(1000));
        this.patrolling = true;
    }
    
    protected boolean isPatrolling() {
        return this.patrolling;
    }
    
    protected void setPatrolling(final boolean boolean1) {
        this.patrolling = boolean1;
    }
    
    public static class LongDistancePatrolGoal<T extends PatrollingMonster> extends Goal {
        private final T mob;
        private final double speedModifier;
        private final double leaderSpeedModifier;
        private long cooldownUntil;
        
        public LongDistancePatrolGoal(final T bcc, final double double2, final double double3) {
            this.mob = bcc;
            this.speedModifier = double2;
            this.leaderSpeedModifier = double3;
            this.cooldownUntil = -1L;
            this.setFlags(EnumSet.<Flag>of(Flag.MOVE));
        }
        
        @Override
        public boolean canUse() {
            final boolean boolean2 = this.mob.level.getGameTime() < this.cooldownUntil;
            return this.mob.isPatrolling() && this.mob.getTarget() == null && !this.mob.isVehicle() && this.mob.hasPatrolTarget() && !boolean2;
        }
        
        @Override
        public void start() {
        }
        
        @Override
        public void stop() {
        }
        
        @Override
        public void tick() {
            final boolean boolean2 = this.mob.isPatrolLeader();
            final PathNavigation awv3 = this.mob.getNavigation();
            if (awv3.isDone()) {
                final List<PatrollingMonster> list4 = this.findPatrolCompanions();
                if (this.mob.isPatrolling() && list4.isEmpty()) {
                    this.mob.setPatrolling(false);
                }
                else if (!boolean2 || !this.mob.getPatrolTarget().closerThan(this.mob.position(), 10.0)) {
                    Vec3 dem5 = Vec3.atBottomCenterOf(this.mob.getPatrolTarget());
                    final Vec3 dem6 = this.mob.position();
                    final Vec3 dem7 = dem6.subtract(dem5);
                    dem5 = dem7.yRot(90.0f).scale(0.4).add(dem5);
                    final Vec3 dem8 = dem5.subtract(dem6).normalize().scale(10.0).add(dem6);
                    BlockPos fu9 = new BlockPos(dem8);
                    fu9 = this.mob.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, fu9);
                    if (!awv3.moveTo(fu9.getX(), fu9.getY(), fu9.getZ(), boolean2 ? this.leaderSpeedModifier : this.speedModifier)) {
                        this.moveRandomly();
                        this.cooldownUntil = this.mob.level.getGameTime() + 200L;
                    }
                    else if (boolean2) {
                        for (final PatrollingMonster bcc11 : list4) {
                            bcc11.setPatrolTarget(fu9);
                        }
                    }
                }
                else {
                    this.mob.findPatrolTarget();
                }
            }
        }
        
        private List<PatrollingMonster> findPatrolCompanions() {
            return this.mob.level.<PatrollingMonster>getEntitiesOfClass(PatrollingMonster.class, this.mob.getBoundingBox().inflate(16.0), bcc -> bcc.canJoinPatrol() && !bcc.is(this.mob));
        }
        
        private boolean moveRandomly() {
            final Random random2 = this.mob.getRandom();
            final BlockPos fu3 = this.mob.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.mob.blockPosition().offset(-8 + random2.nextInt(16), 0, -8 + random2.nextInt(16)));
            return this.mob.getNavigation().moveTo(fu3.getX(), fu3.getY(), fu3.getZ(), this.speedModifier);
        }
    }
}
