package net.minecraft.world.entity.raid;

import java.util.Objects;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.util.RandomPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import java.util.Optional;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import com.google.common.collect.Lists;
import java.util.Iterator;
import net.minecraft.world.entity.monster.AbstractIllager;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import net.minecraft.core.Position;
import java.util.List;
import java.util.EnumSet;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import java.util.Random;
import net.minecraft.sounds.SoundEvent;
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.CompoundTag;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.animal.Wolf;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.ai.goal.PathfindToRaidGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import javax.annotation.Nullable;
import net.minecraft.world.entity.item.ItemEntity;
import java.util.function.Predicate;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.monster.PatrollingMonster;

public abstract class Raider extends PatrollingMonster {
    protected static final EntityDataAccessor<Boolean> IS_CELEBRATING;
    private static final Predicate<ItemEntity> ALLOWED_ITEMS;
    @Nullable
    protected Raid raid;
    private int wave;
    private boolean canJoinRaid;
    private int ticksOutsideRaid;
    
    protected Raider(final EntityType<? extends Raider> aku, final Level bjw) {
        super(aku, bjw);
    }
    
    @Override
    protected void registerGoals() {
        super.registerGoals();
        this.goalSelector.addGoal(1, new ObtainRaidLeaderBannerGoal<>(this));
        this.goalSelector.addGoal(3, new PathfindToRaidGoal<>(this));
        this.goalSelector.addGoal(4, new RaiderMoveThroughVillageGoal(this, 1.0499999523162842, 1));
        this.goalSelector.addGoal(5, new RaiderCelebration(this));
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Boolean>define(Raider.IS_CELEBRATING, false);
    }
    
    public abstract void applyRaidBuffs(final int integer, final boolean boolean2);
    
    public boolean canJoinRaid() {
        return this.canJoinRaid;
    }
    
    public void setCanJoinRaid(final boolean boolean1) {
        this.canJoinRaid = boolean1;
    }
    
    @Override
    public void aiStep() {
        if (this.level instanceof ServerLevel && this.isAlive()) {
            final Raid azn2 = this.getCurrentRaid();
            if (this.canJoinRaid()) {
                if (azn2 == null) {
                    if (this.level.getGameTime() % 20L == 0L) {
                        final Raid azn3 = ((ServerLevel)this.level).getRaidAt(new BlockPos(this));
                        if (azn3 != null && Raids.canJoinRaid(this, azn3)) {
                            azn3.joinRaid(azn3.getGroupsSpawned(), this, null, true);
                        }
                    }
                }
                else {
                    final LivingEntity akz3 = this.getTarget();
                    if (akz3 != null && (akz3.getType() == EntityType.PLAYER || akz3.getType() == EntityType.IRON_GOLEM)) {
                        this.noActionTime = 0;
                    }
                }
            }
        }
        super.aiStep();
    }
    
    @Override
    protected void updateNoActionTime() {
        this.noActionTime += 2;
    }
    
    @Override
    public void die(final DamageSource ajz) {
        if (this.level instanceof ServerLevel) {
            final Entity akq3 = ajz.getEntity();
            final Raid azn4 = this.getCurrentRaid();
            if (azn4 != null) {
                if (this.isPatrolLeader()) {
                    azn4.removeLeader(this.getWave());
                }
                if (akq3 != null && akq3.getType() == EntityType.PLAYER) {
                    azn4.addHeroOfTheVillage(akq3);
                }
                azn4.removeFromRaid(this, false);
            }
            if (this.isPatrolLeader() && azn4 == null && ((ServerLevel)this.level).getRaidAt(new BlockPos(this)) == null) {
                final ItemStack ben5 = this.getItemBySlot(EquipmentSlot.HEAD);
                Player ayj6 = null;
                final Entity akq4 = akq3;
                if (akq4 instanceof Player) {
                    ayj6 = (Player)akq4;
                }
                else if (akq4 instanceof Wolf) {
                    final Wolf auc8 = (Wolf)akq4;
                    final LivingEntity akz9 = auc8.getOwner();
                    if (auc8.isTame() && akz9 instanceof Player) {
                        ayj6 = (Player)akz9;
                    }
                }
                if (!ben5.isEmpty() && ItemStack.matches(ben5, Raid.getLeaderBannerInstance()) && ayj6 != null) {
                    final MobEffectInstance akk8 = ayj6.getEffect(MobEffects.BAD_OMEN);
                    int integer9 = 1;
                    if (akk8 != null) {
                        integer9 += akk8.getAmplifier();
                        ayj6.removeEffectNoUpdate(MobEffects.BAD_OMEN);
                    }
                    else {
                        --integer9;
                    }
                    integer9 = Mth.clamp(integer9, 0, 5);
                    final MobEffectInstance akk9 = new MobEffectInstance(MobEffects.BAD_OMEN, 120000, integer9, false, false, true);
                    if (!this.level.getGameRules().getBoolean(GameRules.RULE_DISABLE_RAIDS)) {
                        ayj6.addEffect(akk9);
                    }
                }
            }
        }
        super.die(ajz);
    }
    
    @Override
    public boolean canJoinPatrol() {
        return !this.hasActiveRaid();
    }
    
    public void setCurrentRaid(@Nullable final Raid azn) {
        this.raid = azn;
    }
    
    @Nullable
    public Raid getCurrentRaid() {
        return this.raid;
    }
    
    public boolean hasActiveRaid() {
        return this.getCurrentRaid() != null && this.getCurrentRaid().isActive();
    }
    
    public void setWave(final int integer) {
        this.wave = integer;
    }
    
    public int getWave() {
        return this.wave;
    }
    
    public boolean isCelebrating() {
        return this.entityData.<Boolean>get(Raider.IS_CELEBRATING);
    }
    
    public void setCelebrating(final boolean boolean1) {
        this.entityData.<Boolean>set(Raider.IS_CELEBRATING, boolean1);
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag jt) {
        super.addAdditionalSaveData(jt);
        jt.putInt("Wave", this.wave);
        jt.putBoolean("CanJoinRaid", this.canJoinRaid);
        if (this.raid != null) {
            jt.putInt("RaidId", this.raid.getId());
        }
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag jt) {
        super.readAdditionalSaveData(jt);
        this.wave = jt.getInt("Wave");
        this.canJoinRaid = jt.getBoolean("CanJoinRaid");
        if (jt.contains("RaidId", 3)) {
            if (this.level instanceof ServerLevel) {
                this.raid = ((ServerLevel)this.level).getRaids().get(jt.getInt("RaidId"));
            }
            if (this.raid != null) {
                this.raid.addWaveMob(this.wave, this, false);
                if (this.isPatrolLeader()) {
                    this.raid.setLeader(this.wave, this);
                }
            }
        }
    }
    
    @Override
    protected void pickUpItem(final ItemEntity awb) {
        final ItemStack ben3 = awb.getItem();
        final boolean boolean4 = this.hasActiveRaid() && this.getCurrentRaid().getLeader(this.getWave()) != null;
        if (this.hasActiveRaid() && !boolean4 && ItemStack.matches(ben3, Raid.getLeaderBannerInstance())) {
            final EquipmentSlot akv5 = EquipmentSlot.HEAD;
            final ItemStack ben4 = this.getItemBySlot(akv5);
            final double double7 = this.getEquipmentDropChance(akv5);
            if (!ben4.isEmpty() && Math.max(this.random.nextFloat() - 0.1f, 0.0f) < double7) {
                this.spawnAtLocation(ben4);
            }
            this.setItemSlot(akv5, ben3);
            this.take(awb, ben3.getCount());
            awb.remove();
            this.getCurrentRaid().setLeader(this.getWave(), this);
            this.setPatrolLeader(true);
        }
        else {
            super.pickUpItem(awb);
        }
    }
    
    @Override
    public boolean removeWhenFarAway(final double double1) {
        return this.getCurrentRaid() == null && super.removeWhenFarAway(double1);
    }
    
    @Override
    public boolean requiresCustomPersistence() {
        return this.getCurrentRaid() != null;
    }
    
    public int getTicksOutsideRaid() {
        return this.ticksOutsideRaid;
    }
    
    public void setTicksOutsideRaid(final int integer) {
        this.ticksOutsideRaid = integer;
    }
    
    @Override
    public boolean hurt(final DamageSource ajz, final float float2) {
        if (this.hasActiveRaid()) {
            this.getCurrentRaid().updateBossbar();
        }
        return super.hurt(ajz, float2);
    }
    
    @Nullable
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bjx, final DifficultyInstance ajj, final MobSpawnType alc, @Nullable final SpawnGroupData alm, @Nullable final CompoundTag jt) {
        this.setCanJoinRaid(this.getType() != EntityType.WITCH || alc != MobSpawnType.NATURAL);
        return super.finalizeSpawn(bjx, ajj, alc, alm, jt);
    }
    
    public abstract SoundEvent getCelebrateSound();
    
    static {
        IS_CELEBRATING = SynchedEntityData.<Boolean>defineId(Raider.class, EntityDataSerializers.BOOLEAN);
        ALLOWED_ITEMS = (awb -> !awb.hasPickUpDelay() && awb.isAlive() && ItemStack.matches(awb.getItem(), Raid.getLeaderBannerInstance()));
    }
    
    public class ObtainRaidLeaderBannerGoal<T extends Raider> extends Goal {
        private final T mob;
        
        public ObtainRaidLeaderBannerGoal(final T azo2) {
            this.mob = azo2;
            this.setFlags(EnumSet.<Flag>of(Flag.MOVE));
        }
        
        @Override
        public boolean canUse() {
            final Raid azn2 = this.mob.getCurrentRaid();
            if (!this.mob.hasActiveRaid() || this.mob.getCurrentRaid().isOver() || !this.mob.canBeLeader() || ItemStack.matches(this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getLeaderBannerInstance())) {
                return false;
            }
            final Raider azo3 = azn2.getLeader(this.mob.getWave());
            if (azo3 == null || !azo3.isAlive()) {
                final List<ItemEntity> list4 = this.mob.level.<ItemEntity>getEntitiesOfClass(ItemEntity.class, this.mob.getBoundingBox().inflate(16.0, 8.0, 16.0), Raider.ALLOWED_ITEMS);
                if (!list4.isEmpty()) {
                    return this.mob.getNavigation().moveTo(list4.get(0), 1.149999976158142);
                }
            }
            return false;
        }
        
        @Override
        public void tick() {
            if (this.mob.getNavigation().getTargetPos().closerThan(this.mob.position(), 1.414)) {
                final List<ItemEntity> list2 = this.mob.level.<ItemEntity>getEntitiesOfClass(ItemEntity.class, this.mob.getBoundingBox().inflate(4.0, 4.0, 4.0), Raider.ALLOWED_ITEMS);
                if (!list2.isEmpty()) {
                    this.mob.pickUpItem(list2.get(0));
                }
            }
        }
    }
    
    public class RaiderCelebration extends Goal {
        private final Raider mob;
        
        RaiderCelebration(final Raider azo2) {
            this.mob = azo2;
            this.setFlags(EnumSet.<Flag>of(Flag.MOVE));
        }
        
        @Override
        public boolean canUse() {
            final Raid azn2 = this.mob.getCurrentRaid();
            return this.mob.isAlive() && this.mob.getTarget() == null && azn2 != null && azn2.isLoss();
        }
        
        @Override
        public void start() {
            this.mob.setCelebrating(true);
            super.start();
        }
        
        @Override
        public void stop() {
            this.mob.setCelebrating(false);
            super.stop();
        }
        
        @Override
        public void tick() {
            if (!this.mob.isSilent() && this.mob.random.nextInt(100) == 0) {
                Raider.this.playSound(Raider.this.getCelebrateSound(), LivingEntity.this.getSoundVolume(), LivingEntity.this.getVoicePitch());
            }
            if (!this.mob.isPassenger() && this.mob.random.nextInt(50) == 0) {
                this.mob.getJumpControl().jump();
            }
            super.tick();
        }
    }
    
    public class HoldGroundAttackGoal extends Goal {
        private final Raider mob;
        private final float hostileRadiusSqr;
        public final TargetingConditions shoutTargeting;
        
        public HoldGroundAttackGoal(final AbstractIllager awe, final float float3) {
            this.shoutTargeting = new TargetingConditions().range(8.0).allowNonAttackable().allowInvulnerable().allowSameTeam().allowUnseeable().ignoreInvisibilityTesting();
            this.mob = awe;
            this.hostileRadiusSqr = float3 * float3;
            this.setFlags(EnumSet.<Flag>of(Flag.MOVE, Flag.LOOK));
        }
        
        @Override
        public boolean canUse() {
            final LivingEntity akz2 = this.mob.getLastHurtByMob();
            return this.mob.getCurrentRaid() == null && this.mob.isPatrolling() && this.mob.getTarget() != null && !this.mob.isAggressive() && (akz2 == null || akz2.getType() != EntityType.PLAYER);
        }
        
        @Override
        public void start() {
            super.start();
            this.mob.getNavigation().stop();
            final List<Raider> list2 = this.mob.level.<Raider>getNearbyEntities(Raider.class, this.shoutTargeting, (LivingEntity)this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0));
            for (final Raider azo4 : list2) {
                azo4.setTarget(this.mob.getTarget());
            }
        }
        
        @Override
        public void stop() {
            super.stop();
            final LivingEntity akz2 = this.mob.getTarget();
            if (akz2 != null) {
                final List<Raider> list3 = this.mob.level.<Raider>getNearbyEntities(Raider.class, this.shoutTargeting, (LivingEntity)this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0));
                for (final Raider azo5 : list3) {
                    azo5.setTarget(akz2);
                    azo5.setAggressive(true);
                }
                this.mob.setAggressive(true);
            }
        }
        
        @Override
        public void tick() {
            final LivingEntity akz2 = this.mob.getTarget();
            if (akz2 == null) {
                return;
            }
            if (this.mob.distanceToSqr(akz2) > this.hostileRadiusSqr) {
                this.mob.getLookControl().setLookAt(akz2, 30.0f, 30.0f);
                if (this.mob.random.nextInt(50) == 0) {
                    this.mob.playAmbientSound();
                }
            }
            else {
                this.mob.setAggressive(true);
            }
            super.tick();
        }
    }
    
    static class RaiderMoveThroughVillageGoal extends Goal {
        private final Raider raider;
        private final double speedModifier;
        private BlockPos poiPos;
        private final List<BlockPos> visited;
        private final int distanceToPoi;
        private boolean stuck;
        
        public RaiderMoveThroughVillageGoal(final Raider azo, final double double2, final int integer) {
            this.visited = Lists.newArrayList();
            this.raider = azo;
            this.speedModifier = double2;
            this.distanceToPoi = integer;
            this.setFlags(EnumSet.<Flag>of(Flag.MOVE));
        }
        
        @Override
        public boolean canUse() {
            this.updateVisited();
            return this.isValidRaid() && this.hasSuitablePoi() && this.raider.getTarget() == null;
        }
        
        private boolean isValidRaid() {
            return this.raider.hasActiveRaid() && !this.raider.getCurrentRaid().isOver();
        }
        
        private boolean hasSuitablePoi() {
            final ServerLevel xd2 = (ServerLevel)this.raider.level;
            final BlockPos fk3 = new BlockPos(this.raider);
            final Optional<BlockPos> optional4 = xd2.getPoiManager().getRandom(asu -> asu == PoiType.HOME, this::hasNotVisited, PoiManager.Occupancy.ANY, fk3, 48, this.raider.random);
            if (!optional4.isPresent()) {
                return false;
            }
            this.poiPos = optional4.get().immutable();
            return true;
        }
        
        @Override
        public boolean canContinueToUse() {
            return !this.raider.getNavigation().isDone() && this.raider.getTarget() == null && !this.poiPos.closerThan(this.raider.position(), this.raider.getBbWidth() + this.distanceToPoi) && !this.stuck;
        }
        
        @Override
        public void stop() {
            if (this.poiPos.closerThan(this.raider.position(), this.distanceToPoi)) {
                this.visited.add(this.poiPos);
            }
        }
        
        @Override
        public void start() {
            super.start();
            this.raider.setNoActionTime(0);
            this.raider.getNavigation().moveTo(this.poiPos.getX(), this.poiPos.getY(), this.poiPos.getZ(), this.speedModifier);
            this.stuck = false;
        }
        
        @Override
        public void tick() {
            if (this.raider.getNavigation().isDone()) {
                final Vec3 cvl2 = new Vec3(this.poiPos);
                Vec3 cvl3 = RandomPos.getPosTowards(this.raider, 16, 7, cvl2, 0.3141592741012573);
                if (cvl3 == null) {
                    cvl3 = RandomPos.getPosTowards(this.raider, 8, 7, cvl2);
                }
                if (cvl3 == null) {
                    this.stuck = true;
                    return;
                }
                this.raider.getNavigation().moveTo(cvl3.x, cvl3.y, cvl3.z, this.speedModifier);
            }
        }
        
        private boolean hasNotVisited(final BlockPos fk) {
            for (final BlockPos fk2 : this.visited) {
                if (Objects.equals(fk, fk2)) {
                    return false;
                }
            }
            return true;
        }
        
        private void updateVisited() {
            if (this.visited.size() > 2) {
                this.visited.remove(0);
            }
        }
    }
}
