package net.minecraft.world.entity.monster;

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.entity.Entity;
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> aku, final Level bjw) {
        super(aku, bjw);
    }
    
    @Override
    protected void registerGoals() {
        super.registerGoals();
        this.goalSelector.addGoal(4, new LongDistancePatrolGoal<>(this, 0.7, 0.595));
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag jt) {
        super.addAdditionalSaveData(jt);
        if (this.patrolTarget != null) {
            jt.put("PatrolTarget", NbtUtils.writeBlockPos(this.patrolTarget));
        }
        jt.putBoolean("PatrolLeader", this.patrolLeader);
        jt.putBoolean("Patrolling", this.patrolling);
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag jt) {
        super.readAdditionalSaveData(jt);
        if (jt.contains("PatrolTarget")) {
            this.patrolTarget = NbtUtils.readBlockPos(jt.getCompound("PatrolTarget"));
        }
        this.patrolLeader = jt.getBoolean("PatrolLeader");
        this.patrolling = jt.getBoolean("Patrolling");
    }
    
    @Override
    public double getRidingHeight() {
        return -0.45;
    }
    
    public boolean canBeLeader() {
        return true;
    }
    
    @Nullable
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bjx, final DifficultyInstance ajj, final MobSpawnType alc, @Nullable final SpawnGroupData alm, @Nullable final CompoundTag jt) {
        if (alc != MobSpawnType.PATROL && alc != MobSpawnType.EVENT && alc != 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 (alc == MobSpawnType.PATROL) {
            this.patrolling = true;
        }
        return super.finalizeSpawn(bjx, ajj, alc, alm, jt);
    }
    
    public static boolean checkPatrollingMonsterSpawnRules(final EntityType<? extends PatrollingMonster> aku, final LevelAccessor bjx, final MobSpawnType alc, final BlockPos fk, final Random random) {
        return bjx.getBrightness(LightLayer.BLOCK, fk) <= 8 && Monster.checkAnyLightMonsterSpawnRules(aku, bjx, alc, fk, random);
    }
    
    @Override
    public boolean removeWhenFarAway(final double double1) {
        return !this.patrolling || double1 > 16384.0;
    }
    
    public void setPatrolTarget(final BlockPos fk) {
        this.patrolTarget = fk;
        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 = new BlockPos(this).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 awx, final double double2, final double double3) {
            this.mob = awx;
            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 arr3 = this.mob.getNavigation();
            if (arr3.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 cvl5 = new Vec3(this.mob.getPatrolTarget());
                    final Vec3 cvl6 = this.mob.position();
                    final Vec3 cvl7 = cvl6.subtract(cvl5);
                    cvl5 = cvl7.yRot(90.0f).scale(0.4).add(cvl5);
                    final Vec3 cvl8 = cvl5.subtract(cvl6).normalize().scale(10.0).add(cvl6);
                    BlockPos fk9 = new BlockPos(cvl8);
                    fk9 = this.mob.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, fk9);
                    if (!arr3.moveTo(fk9.getX(), fk9.getY(), fk9.getZ(), boolean2 ? this.leaderSpeedModifier : this.speedModifier)) {
                        this.moveRandomly();
                        this.cooldownUntil = this.mob.level.getGameTime() + 200L;
                    }
                    else if (boolean2) {
                        for (final PatrollingMonster awx11 : list4) {
                            awx11.setPatrolTarget(fk9);
                        }
                    }
                }
                else {
                    this.mob.findPatrolTarget();
                }
            }
        }
        
        private List<PatrollingMonster> findPatrolCompanions() {
            return this.mob.level.<PatrollingMonster>getEntitiesOfClass(PatrollingMonster.class, this.mob.getBoundingBox().inflate(16.0), awx -> awx.canJoinPatrol() && !awx.is(this.mob));
        }
        
        private boolean moveRandomly() {
            final Random random2 = this.mob.getRandom();
            final BlockPos fk3 = this.mob.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, new BlockPos(this.mob).offset(-8 + random2.nextInt(16), 0, -8 + random2.nextInt(16)));
            return this.mob.getNavigation().moveTo(fk3.getX(), fk3.getY(), fk3.getZ(), this.speedModifier);
        }
    }
}
