package net.minecraft.world.entity.monster;

import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
import net.minecraft.world.level.pathfinder.PathFinder;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.AgableMob;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.level.block.Block;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.ai.attributes.Attributes;
import com.google.common.collect.UnmodifiableIterator;
import java.util.Iterator;
import java.util.Set;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.vehicle.DismountHelper;
import net.minecraft.world.entity.Pose;
import com.google.common.collect.Sets;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.item.Items;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
import net.minecraft.world.entity.ai.goal.FollowParentGoal;
import net.minecraft.world.entity.ai.goal.BreedGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.sounds.SoundEvents;
import javax.annotation.Nullable;
import net.minecraft.sounds.SoundSource;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.Direction;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.tags.Tag;
import net.minecraft.tags.FluidTags;
import java.util.Random;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.goal.PanicGoal;
import net.minecraft.world.entity.ai.goal.TemptGoal;
import net.minecraft.world.entity.ItemBasedSteering;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.entity.Saddleable;
import net.minecraft.world.entity.ItemSteerable;
import net.minecraft.world.entity.animal.Animal;

public class Strider extends Animal implements ItemSteerable, Saddleable {
    private static final Ingredient FOOD_ITEMS;
    private static final Ingredient TEMPT_ITEMS;
    private static final EntityDataAccessor<Integer> DATA_BOOST_TIME;
    private static final EntityDataAccessor<Boolean> DATA_SUFFOCATING;
    private static final EntityDataAccessor<Boolean> DATA_SADDLE_ID;
    private final ItemBasedSteering steering;
    private TemptGoal temptGoal;
    private PanicGoal panicGoal;
    
    public Strider(final EntityType<? extends Strider> aoq, final Level bqb) {
        super(aoq, bqb);
        this.steering = new ItemBasedSteering(this.entityData, Strider.DATA_BOOST_TIME, Strider.DATA_SADDLE_ID);
        this.blocksBuilding = true;
        this.setPathfindingMalus(BlockPathTypes.WATER, -1.0f);
        this.setPathfindingMalus(BlockPathTypes.LAVA, 0.0f);
        this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0f);
        this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0f);
    }
    
    public static boolean checkStriderSpawnRules(final EntityType<Strider> aoq, final LevelAccessor bqc, final MobSpawnType apb, final BlockPos fu, final Random random) {
        final BlockPos.MutableBlockPos a6 = fu.mutable();
        do {
            a6.move(Direction.UP);
        } while (bqc.getFluidState(a6).is(FluidTags.LAVA));
        return bqc.getBlockState(a6).isAir();
    }
    
    @Override
    public void onSyncedDataUpdated(final EntityDataAccessor<?> tq) {
        if (Strider.DATA_BOOST_TIME.equals(tq) && this.level.isClientSide) {
            this.steering.onSynced();
        }
        super.onSyncedDataUpdated(tq);
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Integer>define(Strider.DATA_BOOST_TIME, 0);
        this.entityData.<Boolean>define(Strider.DATA_SUFFOCATING, false);
        this.entityData.<Boolean>define(Strider.DATA_SADDLE_ID, false);
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag le) {
        super.addAdditionalSaveData(le);
        this.steering.addAdditionalSaveData(le);
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag le) {
        super.readAdditionalSaveData(le);
        this.steering.readAdditionalSaveData(le);
    }
    
    @Override
    public boolean isSaddled() {
        return this.steering.hasSaddle();
    }
    
    @Override
    public boolean isSaddleable() {
        return this.isAlive() && !this.isBaby();
    }
    
    @Override
    public void equipSaddle(@Nullable final SoundSource acm) {
        this.steering.setSaddle(true);
        if (acm != null) {
            this.level.playSound(null, this, SoundEvents.STRIDER_SADDLE, acm, 0.5f, 1.0f);
        }
    }
    
    @Override
    protected void registerGoals() {
        this.panicGoal = new PanicGoal(this, 1.65);
        this.goalSelector.addGoal(1, this.panicGoal);
        this.goalSelector.addGoal(3, new BreedGoal(this, 1.0));
        this.temptGoal = new TemptGoal(this, 1.4, false, Strider.TEMPT_ITEMS);
        this.goalSelector.addGoal(4, this.temptGoal);
        this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.1));
        this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0, 60));
        this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0f));
        this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
        this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Strider.class, 8.0f));
    }
    
    public void setSuffocating(final boolean boolean1) {
        this.entityData.<Boolean>set(Strider.DATA_SUFFOCATING, boolean1);
    }
    
    public boolean isSuffocating() {
        if (this.getVehicle() instanceof Strider) {
            return ((Strider)this.getVehicle()).isSuffocating();
        }
        return this.entityData.<Boolean>get(Strider.DATA_SUFFOCATING);
    }
    
    @Override
    public boolean canStandOnFluid(final Fluid cwz) {
        return cwz.is(FluidTags.LAVA);
    }
    
    @Nullable
    @Override
    public AABB getCollideAgainstBox(final Entity aom) {
        if (aom.isPushable()) {
            return aom.getBoundingBox();
        }
        return null;
    }
    
    @Override
    public boolean isPushable() {
        return true;
    }
    
    @Override
    public double getPassengersRidingOffset() {
        final float float2 = Math.min(0.25f, this.animationSpeed);
        final float float3 = this.animationPosition;
        return this.getBbHeight() - 0.2 + 0.12f * Mth.cos(float3 * 1.5f) * 2.0f * float2;
    }
    
    @Override
    public boolean canBeControlledByRider() {
        final Entity aom2 = this.getControllingPassenger();
        if (!(aom2 instanceof Player)) {
            return false;
        }
        final Player bec3 = (Player)aom2;
        return bec3.getMainHandItem().getItem() == Items.WARPED_FUNGUS_ON_A_STICK || bec3.getOffhandItem().getItem() == Items.WARPED_FUNGUS_ON_A_STICK;
    }
    
    @Override
    public boolean checkSpawnObstruction(final LevelReader bqd) {
        return bqd.isUnobstructed(this);
    }
    
    @Nullable
    @Override
    public Entity getControllingPassenger() {
        if (this.getPassengers().isEmpty()) {
            return null;
        }
        return this.getPassengers().get(0);
    }
    
    @Override
    public Vec3 getDismountLocationForPassenger(final LivingEntity aoy) {
        final Vec3[] arr3 = { Entity.getCollisionHorizontalEscapeVector(this.getBbWidth(), aoy.getBbWidth(), aoy.yRot), Entity.getCollisionHorizontalEscapeVector(this.getBbWidth(), aoy.getBbWidth(), aoy.yRot - 22.5f), Entity.getCollisionHorizontalEscapeVector(this.getBbWidth(), aoy.getBbWidth(), aoy.yRot + 22.5f), Entity.getCollisionHorizontalEscapeVector(this.getBbWidth(), aoy.getBbWidth(), aoy.yRot - 45.0f), Entity.getCollisionHorizontalEscapeVector(this.getBbWidth(), aoy.getBbWidth(), aoy.yRot + 45.0f) };
        final Set<BlockPos> set4 = Sets.newLinkedHashSet();
        final double double5 = this.getBoundingBox().maxY;
        final double double6 = this.getBoundingBox().minY - 0.5;
        final BlockPos.MutableBlockPos a9 = new BlockPos.MutableBlockPos();
        for (final Vec3 dem13 : arr3) {
            a9.set(this.getX() + dem13.x, double5, this.getZ() + dem13.z);
            for (double double7 = double5; double7 > double6; --double7) {
                set4.add(a9.immutable());
                a9.move(Direction.DOWN);
            }
        }
        for (final BlockPos fu11 : set4) {
            if (this.level.getFluidState(fu11).is(FluidTags.LAVA)) {
                continue;
            }
            for (final Pose apj13 : aoy.getDismountPoses()) {
                final double double7 = this.level.getRelativeFloorHeight(fu11);
                if (DismountHelper.isFloorValid(double7)) {
                    final AABB deg16 = aoy.getLocalBoundsForPose(apj13);
                    final Vec3 dem14 = Vec3.upFromBottomCenterOf(fu11, double7);
                    if (DismountHelper.canDismountTo(this.level, aoy, deg16.move(dem14))) {
                        aoy.setPose(apj13);
                        return dem14;
                    }
                    continue;
                }
            }
        }
        return new Vec3(this.getX(), this.getBoundingBox().maxY, this.getZ());
    }
    
    @Override
    public void travel(final Vec3 dem) {
        this.setSpeed(this.getMoveSpeed());
        this.travel(this, this.steering, dem);
    }
    
    public float getMoveSpeed() {
        return (float)this.getAttributeValue(Attributes.MOVEMENT_SPEED) * (this.isSuffocating() ? 0.66f : 1.0f);
    }
    
    @Override
    public float getSteeringSpeed() {
        return (float)this.getAttributeValue(Attributes.MOVEMENT_SPEED) * (this.isSuffocating() ? 0.23f : 0.55f);
    }
    
    @Override
    public void travelWithInput(final Vec3 dem) {
        super.travel(dem);
    }
    
    @Override
    protected float nextStep() {
        return this.moveDist + 0.6f;
    }
    
    @Override
    protected void playStepSound(final BlockPos fu, final BlockState cfj) {
        this.playSound(this.isInLava() ? SoundEvents.STRIDER_STEP_LAVA : SoundEvents.STRIDER_STEP, 1.0f, 1.0f);
    }
    
    @Override
    public boolean boost() {
        return this.steering.boost(this.getRandom());
    }
    
    @Override
    protected void checkFallDamage(final double double1, final boolean boolean2, final BlockState cfj, final BlockPos fu) {
        this.checkInsideBlocks();
        if (this.isInLava()) {
            this.fallDistance = 0.0f;
            return;
        }
        super.checkFallDamage(double1, boolean2, cfj, fu);
    }
    
    @Override
    public void tick() {
        if (this.isBeingTempted() && this.random.nextInt(140) == 0) {
            this.playSound(SoundEvents.STRIDER_HAPPY, 1.0f, this.getVoicePitch());
        }
        else if (this.isPanicking() && this.random.nextInt(60) == 0) {
            this.playSound(SoundEvents.STRIDER_RETREAT, 1.0f, this.getVoicePitch());
        }
        final BlockState cfj2 = this.level.getBlockState(this.blockPosition());
        final BlockState cfj3 = this.getBlockStateOn();
        final boolean boolean4 = cfj2.is(BlockTags.STRIDER_WARM_BLOCKS) || cfj3.is(BlockTags.STRIDER_WARM_BLOCKS) || this.getFluidHeight(FluidTags.LAVA) > 0.0;
        this.setSuffocating(!boolean4);
        super.tick();
        this.floatStrider();
        this.checkInsideBlocks();
    }
    
    private boolean isPanicking() {
        return this.panicGoal != null && this.panicGoal.isRunning();
    }
    
    private boolean isBeingTempted() {
        return this.temptGoal != null && this.temptGoal.isRunning();
    }
    
    @Override
    protected boolean shouldPassengersInheritMalus() {
        return true;
    }
    
    private void floatStrider() {
        if (this.isInLava()) {
            final CollisionContext der2 = CollisionContext.of(this);
            if (!der2.isAbove(LiquidBlock.STABLE_SHAPE, this.blockPosition(), true) || this.level.getFluidState(this.blockPosition().above()).is(FluidTags.LAVA)) {
                this.setDeltaMovement(this.getDeltaMovement().scale(0.5).add(0.0, 0.05, 0.0));
            }
            else {
                this.onGround = true;
            }
        }
    }
    
    public static AttributeSupplier.Builder createAttributes() {
        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.17499999701976776).add(Attributes.FOLLOW_RANGE, 16.0);
    }
    
    @Override
    protected SoundEvent getAmbientSound() {
        if (this.isPanicking() || this.isBeingTempted()) {
            return null;
        }
        return SoundEvents.STRIDER_AMBIENT;
    }
    
    @Override
    protected SoundEvent getHurtSound(final DamageSource anw) {
        return SoundEvents.STRIDER_HURT;
    }
    
    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.STRIDER_DEATH;
    }
    
    @Override
    protected boolean canAddPassenger(final Entity aom) {
        return this.getPassengers().isEmpty() && !this.isEyeInFluid(FluidTags.LAVA);
    }
    
    @Override
    public boolean isSensitiveToWater() {
        return true;
    }
    
    @Override
    public boolean isOnFire() {
        return false;
    }
    
    @Override
    protected PathNavigation createNavigation(final Level bqb) {
        return new StriderPathNavigation(this, bqb);
    }
    
    @Override
    public float getWalkTargetValue(final BlockPos fu, final LevelReader bqd) {
        if (bqd.getBlockState(fu).getFluidState().is(FluidTags.LAVA)) {
            return 10.0f;
        }
        return 0.0f;
    }
    
    @Override
    public Strider getBreedOffspring(final AgableMob aok) {
        return EntityType.STRIDER.create(this.level);
    }
    
    @Override
    public boolean isFood(final ItemStack bki) {
        return Strider.FOOD_ITEMS.test(bki);
    }
    
    @Override
    protected void dropEquipment() {
        super.dropEquipment();
        if (this.isSaddled()) {
            this.spawnAtLocation(Items.SADDLE);
        }
    }
    
    @Override
    public InteractionResult mobInteract(final Player bec, final InteractionHand anf) {
        final boolean boolean4 = this.isFood(bec.getItemInHand(anf));
        if (!boolean4 && this.isSaddled() && !this.isVehicle()) {
            if (!this.level.isClientSide) {
                bec.startRiding(this);
            }
            return InteractionResult.sidedSuccess(this.level.isClientSide);
        }
        final InteractionResult ang5 = super.mobInteract(bec, anf);
        if (ang5.consumesAction()) {
            if (boolean4 && !this.isSilent()) {
                this.level.playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0f, 1.0f + (this.random.nextFloat() - this.random.nextFloat()) * 0.2f);
            }
            return ang5;
        }
        final ItemStack bki6 = bec.getItemInHand(anf);
        if (bki6.getItem() == Items.SADDLE) {
            return bki6.interactLivingEntity(bec, this, anf);
        }
        return InteractionResult.PASS;
    }
    
    @Override
    public Vec3 getLeashOffset() {
        return new Vec3(0.0, 0.6f * this.getEyeHeight(), this.getBbWidth() * 0.4f);
    }
    
    @Nullable
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bqc, final DifficultyInstance ane, final MobSpawnType apb, @Nullable SpawnGroupData apo, @Nullable final CompoundTag le) {
        SpawnGroupData apo2 = null;
        StriderGroupData.Rider a7;
        if (apo instanceof StriderGroupData) {
            a7 = ((StriderGroupData)apo).rider;
        }
        else if (!this.isBaby()) {
            if (this.random.nextInt(30) == 0) {
                a7 = StriderGroupData.Rider.PIGLIN_RIDER;
                apo2 = new Zombie.ZombieGroupData(Zombie.getSpawnAsBabyOdds(this.random), false);
            }
            else if (this.random.nextInt(10) == 0) {
                a7 = StriderGroupData.Rider.BABY_RIDER;
            }
            else {
                a7 = StriderGroupData.Rider.NO_RIDER;
            }
            apo = new StriderGroupData(a7);
            ((AgableMobGroupData)apo).setBabySpawnChance((a7 == StriderGroupData.Rider.NO_RIDER) ? 0.5f : 0.0f);
        }
        else {
            a7 = StriderGroupData.Rider.NO_RIDER;
        }
        Mob aoz9 = null;
        if (a7 == StriderGroupData.Rider.BABY_RIDER) {
            final Strider bco10 = EntityType.STRIDER.create(bqc.getLevel());
            if (bco10 != null) {
                aoz9 = bco10;
                bco10.setAge(-24000);
            }
        }
        else if (a7 == StriderGroupData.Rider.PIGLIN_RIDER) {
            final ZombifiedPiglin bcw10 = EntityType.ZOMBIFIED_PIGLIN.create(bqc.getLevel());
            if (bcw10 != null) {
                aoz9 = bcw10;
                this.equipSaddle(null);
            }
        }
        if (aoz9 != null) {
            aoz9.moveTo(this.getX(), this.getY(), this.getZ(), this.yRot, 0.0f);
            aoz9.finalizeSpawn(bqc, ane, MobSpawnType.JOCKEY, apo2, null);
            aoz9.startRiding(this, true);
            bqc.addFreshEntity(aoz9);
        }
        return super.finalizeSpawn(bqc, ane, apb, apo, le);
    }
    
    static {
        FOOD_ITEMS = Ingredient.of(Items.WARPED_FUNGUS);
        TEMPT_ITEMS = Ingredient.of(Items.WARPED_FUNGUS, Items.WARPED_FUNGUS_ON_A_STICK);
        DATA_BOOST_TIME = SynchedEntityData.<Integer>defineId(Strider.class, EntityDataSerializers.INT);
        DATA_SUFFOCATING = SynchedEntityData.<Boolean>defineId(Strider.class, EntityDataSerializers.BOOLEAN);
        DATA_SADDLE_ID = SynchedEntityData.<Boolean>defineId(Strider.class, EntityDataSerializers.BOOLEAN);
    }
    
    public static class StriderGroupData extends AgableMobGroupData {
        public final Rider rider;
        
        public StriderGroupData(final Rider a) {
            this.rider = a;
        }
        
        public enum Rider {
            NO_RIDER, 
            BABY_RIDER, 
            PIGLIN_RIDER;
        }
    }
    
    static class StriderPathNavigation extends GroundPathNavigation {
        StriderPathNavigation(final Strider bco, final Level bqb) {
            super(bco, bqb);
        }
        
        @Override
        protected PathFinder createPathFinder(final int integer) {
            this.nodeEvaluator = new WalkNodeEvaluator();
            return new PathFinder(this.nodeEvaluator, integer);
        }
        
        @Override
        protected boolean hasValidPathType(final BlockPathTypes czb) {
            return czb == BlockPathTypes.LAVA || czb == BlockPathTypes.DAMAGE_FIRE || czb == BlockPathTypes.DANGER_FIRE || super.hasValidPathType(czb);
        }
        
        @Override
        public boolean isStableDestination(final BlockPos fu) {
            return this.level.getBlockState(fu).is(Blocks.LAVA) || super.isStableDestination(fu);
        }
    }
}
