package net.minecraft.world.entity.animal;

import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.util.TimeUtil;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import java.util.Random;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraft.world.entity.monster.Ghast;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.AgableMob;
import javax.annotation.Nullable;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.DyeItem;
import net.minecraft.world.item.Items;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal;
import net.minecraft.world.entity.monster.AbstractSkeleton;
import net.minecraft.world.entity.ai.goal.target.NonTameRandomTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal;
import net.minecraft.world.entity.ai.goal.target.OwnerHurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.ai.goal.BegGoal;
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
import net.minecraft.world.entity.ai.goal.BreedGoal;
import net.minecraft.world.entity.ai.goal.FollowOwnerGoal;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
import net.minecraft.world.entity.animal.horse.Llama;
import net.minecraft.world.entity.ai.goal.SitWhenOrderedToGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import java.util.UUID;
import net.minecraft.util.IntRange;
import net.minecraft.world.entity.LivingEntity;
import java.util.function.Predicate;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.TamableAnimal;

public class Wolf extends TamableAnimal implements NeutralMob {
    private static final EntityDataAccessor<Boolean> DATA_INTERESTED_ID;
    private static final EntityDataAccessor<Integer> DATA_COLLAR_COLOR;
    private static final EntityDataAccessor<Integer> DATA_REMAINING_ANGER_TIME;
    public static final Predicate<LivingEntity> PREY_SELECTOR;
    private float interestedAngle;
    private float interestedAngleO;
    private boolean isWet;
    private boolean isShaking;
    private float shakeAnim;
    private float shakeAnimO;
    private static final IntRange PERSISTENT_ANGER_TIME;
    private UUID persistentAngerTarget;
    
    public Wolf(final EntityType<? extends Wolf> aoq, final Level bqb) {
        super(aoq, bqb);
        this.setTame(false);
    }
    
    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(1, new FloatGoal(this));
        this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
        this.goalSelector.addGoal(3, new WolfAvoidEntityGoal<>(this, Llama.class, 24.0f, 1.5, 1.5));
        this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4f));
        this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, true));
        this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0, 10.0f, 2.0f, false));
        this.goalSelector.addGoal(7, new BreedGoal(this, 1.0));
        this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 1.0));
        this.goalSelector.addGoal(9, new BegGoal(this, 8.0f));
        this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0f));
        this.goalSelector.addGoal(10, new RandomLookAroundGoal(this));
        this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
        this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
        this.targetSelector.addGoal(3, new HurtByTargetGoal(this, new Class[0]).setAlertOthers(new Class[0]));
        this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
        this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR));
        this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
        this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false));
        this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true));
    }
    
    public static AttributeSupplier.Builder createAttributes() {
        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896).add(Attributes.MAX_HEALTH, 8.0).add(Attributes.ATTACK_DAMAGE, 2.0);
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Boolean>define(Wolf.DATA_INTERESTED_ID, false);
        this.entityData.<Integer>define(Wolf.DATA_COLLAR_COLOR, DyeColor.RED.getId());
        this.entityData.<Integer>define(Wolf.DATA_REMAINING_ANGER_TIME, 0);
    }
    
    @Override
    protected void playStepSound(final BlockPos fu, final BlockState cfj) {
        this.playSound(SoundEvents.WOLF_STEP, 0.15f, 1.0f);
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag le) {
        super.addAdditionalSaveData(le);
        le.putByte("CollarColor", (byte)this.getCollarColor().getId());
        this.addPersistentAngerSaveData(le);
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag le) {
        super.readAdditionalSaveData(le);
        if (le.contains("CollarColor", 99)) {
            this.setCollarColor(DyeColor.byId(le.getInt("CollarColor")));
        }
        this.readPersistentAngerSaveData((ServerLevel)this.level, le);
    }
    
    @Override
    protected SoundEvent getAmbientSound() {
        if (this.isAngry()) {
            return SoundEvents.WOLF_GROWL;
        }
        if (this.random.nextInt(3) != 0) {
            return SoundEvents.WOLF_AMBIENT;
        }
        if (this.isTame() && this.getHealth() < 10.0f) {
            return SoundEvents.WOLF_WHINE;
        }
        return SoundEvents.WOLF_PANT;
    }
    
    @Override
    protected SoundEvent getHurtSound(final DamageSource anw) {
        return SoundEvents.WOLF_HURT;
    }
    
    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.WOLF_DEATH;
    }
    
    @Override
    protected float getSoundVolume() {
        return 0.4f;
    }
    
    @Override
    public void aiStep() {
        super.aiStep();
        if (!this.level.isClientSide && this.isWet && !this.isShaking && !this.isPathFinding() && this.onGround) {
            this.isShaking = true;
            this.shakeAnim = 0.0f;
            this.shakeAnimO = 0.0f;
            this.level.broadcastEntityEvent(this, (byte)8);
        }
        if (!this.level.isClientSide) {
            this.updatePersistentAnger((ServerLevel)this.level, true);
        }
    }
    
    @Override
    public void tick() {
        super.tick();
        if (!this.isAlive()) {
            return;
        }
        this.interestedAngleO = this.interestedAngle;
        if (this.isInterested()) {
            this.interestedAngle += (1.0f - this.interestedAngle) * 0.4f;
        }
        else {
            this.interestedAngle += (0.0f - this.interestedAngle) * 0.4f;
        }
        if (this.isInWaterRainOrBubble()) {
            this.isWet = true;
            this.isShaking = false;
            this.shakeAnim = 0.0f;
            this.shakeAnimO = 0.0f;
        }
        else if ((this.isWet || this.isShaking) && this.isShaking) {
            if (this.shakeAnim == 0.0f) {
                this.playSound(SoundEvents.WOLF_SHAKE, this.getSoundVolume(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2f + 1.0f);
            }
            this.shakeAnimO = this.shakeAnim;
            this.shakeAnim += 0.05f;
            if (this.shakeAnimO >= 2.0f) {
                this.isWet = false;
                this.isShaking = false;
                this.shakeAnimO = 0.0f;
                this.shakeAnim = 0.0f;
            }
            if (this.shakeAnim > 0.4f) {
                final float float2 = (float)this.getY();
                final int integer3 = (int)(Mth.sin((this.shakeAnim - 0.4f) * 3.1415927f) * 7.0f);
                final Vec3 dem4 = this.getDeltaMovement();
                for (int integer4 = 0; integer4 < integer3; ++integer4) {
                    final float float3 = (this.random.nextFloat() * 2.0f - 1.0f) * this.getBbWidth() * 0.5f;
                    final float float4 = (this.random.nextFloat() * 2.0f - 1.0f) * this.getBbWidth() * 0.5f;
                    this.level.addParticle(ParticleTypes.SPLASH, this.getX() + float3, float2 + 0.8f, this.getZ() + float4, dem4.x, dem4.y, dem4.z);
                }
            }
        }
    }
    
    @Override
    public void die(final DamageSource anw) {
        this.isWet = false;
        this.isShaking = false;
        this.shakeAnimO = 0.0f;
        this.shakeAnim = 0.0f;
        super.die(anw);
    }
    
    public boolean isWet() {
        return this.isWet;
    }
    
    public float getWetShade(final float float1) {
        return 0.75f + Mth.lerp(float1, this.shakeAnimO, this.shakeAnim) / 2.0f * 0.25f;
    }
    
    public float getBodyRollAngle(final float float1, final float float2) {
        float float3 = (Mth.lerp(float1, this.shakeAnimO, this.shakeAnim) + float2) / 1.8f;
        if (float3 < 0.0f) {
            float3 = 0.0f;
        }
        else if (float3 > 1.0f) {
            float3 = 1.0f;
        }
        return Mth.sin(float3 * 3.1415927f) * Mth.sin(float3 * 3.1415927f * 11.0f) * 0.15f * 3.1415927f;
    }
    
    public float getHeadRollAngle(final float float1) {
        return Mth.lerp(float1, this.interestedAngleO, this.interestedAngle) * 0.15f * 3.1415927f;
    }
    
    @Override
    protected float getStandingEyeHeight(final Pose apj, final EntityDimensions aon) {
        return aon.height * 0.8f;
    }
    
    @Override
    public int getMaxHeadXRot() {
        if (this.isInSittingPose()) {
            return 20;
        }
        return super.getMaxHeadXRot();
    }
    
    @Override
    public boolean hurt(final DamageSource anw, float float2) {
        if (this.isInvulnerableTo(anw)) {
            return false;
        }
        final Entity aom4 = anw.getEntity();
        this.setOrderedToSit(false);
        if (aom4 != null && !(aom4 instanceof Player) && !(aom4 instanceof AbstractArrow)) {
            float2 = (float2 + 1.0f) / 2.0f;
        }
        return super.hurt(anw, float2);
    }
    
    @Override
    public boolean doHurtTarget(final Entity aom) {
        final boolean boolean3 = aom.hurt(DamageSource.mobAttack(this), (float)(int)this.getAttributeValue(Attributes.ATTACK_DAMAGE));
        if (boolean3) {
            this.doEnchantDamageEffects(this, aom);
        }
        return boolean3;
    }
    
    @Override
    public void setTame(final boolean boolean1) {
        super.setTame(boolean1);
        if (boolean1) {
            this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(20.0);
            this.setHealth(20.0f);
        }
        else {
            this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(8.0);
        }
        this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(4.0);
    }
    
    @Override
    public InteractionResult mobInteract(final Player bec, final InteractionHand anf) {
        final ItemStack bki4 = bec.getItemInHand(anf);
        final Item bke5 = bki4.getItem();
        if (this.level.isClientSide) {
            final boolean boolean6 = this.isOwnedBy(bec) || this.isTame() || (bke5 == Items.BONE && !this.isTame() && !this.isAngry());
            return boolean6 ? InteractionResult.CONSUME : InteractionResult.PASS;
        }
        if (this.isTame()) {
            if (this.isFood(bki4) && this.getHealth() < this.getMaxHealth()) {
                if (!bec.abilities.instabuild) {
                    bki4.shrink(1);
                }
                this.heal((float)bke5.getFoodProperties().getNutrition());
                return InteractionResult.SUCCESS;
            }
            if (bke5 instanceof DyeItem) {
                final DyeColor bje6 = ((DyeItem)bke5).getDyeColor();
                if (bje6 != this.getCollarColor()) {
                    this.setCollarColor(bje6);
                    if (!bec.abilities.instabuild) {
                        bki4.shrink(1);
                    }
                    return InteractionResult.SUCCESS;
                }
            }
            else {
                final InteractionResult ang6 = super.mobInteract(bec, anf);
                if ((!ang6.consumesAction() || this.isBaby()) && this.isOwnedBy(bec)) {
                    this.setOrderedToSit(!this.isOrderedToSit());
                    this.jumping = false;
                    this.navigation.stop();
                    this.setTarget(null);
                    return InteractionResult.SUCCESS;
                }
                return ang6;
            }
        }
        else if (bke5 == Items.BONE && !this.isAngry()) {
            if (!bec.abilities.instabuild) {
                bki4.shrink(1);
            }
            if (this.random.nextInt(3) == 0) {
                this.tame(bec);
                this.navigation.stop();
                this.setTarget(null);
                this.setOrderedToSit(true);
                this.level.broadcastEntityEvent(this, (byte)7);
            }
            else {
                this.level.broadcastEntityEvent(this, (byte)6);
            }
            return InteractionResult.SUCCESS;
        }
        return super.mobInteract(bec, anf);
    }
    
    @Override
    public void handleEntityEvent(final byte byte1) {
        if (byte1 == 8) {
            this.isShaking = true;
            this.shakeAnim = 0.0f;
            this.shakeAnimO = 0.0f;
        }
        else {
            super.handleEntityEvent(byte1);
        }
    }
    
    public float getTailAngle() {
        if (this.isAngry()) {
            return 1.5393804f;
        }
        if (this.isTame()) {
            return (0.55f - (this.getMaxHealth() - this.getHealth()) * 0.02f) * 3.1415927f;
        }
        return 0.62831855f;
    }
    
    @Override
    public boolean isFood(final ItemStack bki) {
        final Item bke3 = bki.getItem();
        return bke3.isEdible() && bke3.getFoodProperties().isMeat();
    }
    
    @Override
    public int getMaxSpawnClusterSize() {
        return 8;
    }
    
    @Override
    public int getRemainingPersistentAngerTime() {
        return this.entityData.<Integer>get(Wolf.DATA_REMAINING_ANGER_TIME);
    }
    
    @Override
    public void setRemainingPersistentAngerTime(final int integer) {
        this.entityData.<Integer>set(Wolf.DATA_REMAINING_ANGER_TIME, integer);
    }
    
    @Override
    public void startPersistentAngerTimer() {
        this.setRemainingPersistentAngerTime(Wolf.PERSISTENT_ANGER_TIME.randomValue(this.random));
    }
    
    @Nullable
    @Override
    public UUID getPersistentAngerTarget() {
        return this.persistentAngerTarget;
    }
    
    @Override
    public void setPersistentAngerTarget(@Nullable final UUID uUID) {
        this.persistentAngerTarget = uUID;
    }
    
    public DyeColor getCollarColor() {
        return DyeColor.byId(this.entityData.<Integer>get(Wolf.DATA_COLLAR_COLOR));
    }
    
    public void setCollarColor(final DyeColor bje) {
        this.entityData.<Integer>set(Wolf.DATA_COLLAR_COLOR, bje.getId());
    }
    
    @Override
    public Wolf getBreedOffspring(final AgableMob aok) {
        final Wolf azk3 = EntityType.WOLF.create(this.level);
        final UUID uUID4 = this.getOwnerUUID();
        if (uUID4 != null) {
            azk3.setOwnerUUID(uUID4);
            azk3.setTame(true);
        }
        return azk3;
    }
    
    public void setIsInterested(final boolean boolean1) {
        this.entityData.<Boolean>set(Wolf.DATA_INTERESTED_ID, boolean1);
    }
    
    @Override
    public boolean canMate(final Animal ayk) {
        if (ayk == this) {
            return false;
        }
        if (!this.isTame()) {
            return false;
        }
        if (!(ayk instanceof Wolf)) {
            return false;
        }
        final Wolf azk3 = (Wolf)ayk;
        return azk3.isTame() && !azk3.isInSittingPose() && this.isInLove() && azk3.isInLove();
    }
    
    public boolean isInterested() {
        return this.entityData.<Boolean>get(Wolf.DATA_INTERESTED_ID);
    }
    
    @Override
    public boolean wantsToAttack(final LivingEntity aoy1, final LivingEntity aoy2) {
        if (aoy1 instanceof Creeper || aoy1 instanceof Ghast) {
            return false;
        }
        if (aoy1 instanceof Wolf) {
            final Wolf azk4 = (Wolf)aoy1;
            return !azk4.isTame() || azk4.getOwner() != aoy2;
        }
        return (!(aoy1 instanceof Player) || !(aoy2 instanceof Player) || ((Player)aoy2).canHarmPlayer((Player)aoy1)) && (!(aoy1 instanceof AbstractHorse) || !((AbstractHorse)aoy1).isTamed()) && (!(aoy1 instanceof TamableAnimal) || !((TamableAnimal)aoy1).isTame());
    }
    
    @Override
    public boolean canBeLeashed(final Player bec) {
        return !this.isAngry() && super.canBeLeashed(bec);
    }
    
    @Override
    public Vec3 getLeashOffset() {
        return new Vec3(0.0, 0.6f * this.getEyeHeight(), this.getBbWidth() * 0.4f);
    }
    
    static {
        DATA_INTERESTED_ID = SynchedEntityData.<Boolean>defineId(Wolf.class, EntityDataSerializers.BOOLEAN);
        DATA_COLLAR_COLOR = SynchedEntityData.<Integer>defineId(Wolf.class, EntityDataSerializers.INT);
        DATA_REMAINING_ANGER_TIME = SynchedEntityData.<Integer>defineId(Wolf.class, EntityDataSerializers.INT);
        final EntityType<?> aoq2;
        PREY_SELECTOR = (aoy -> {
            aoq2 = aoy.getType();
            return aoq2 == EntityType.SHEEP || aoq2 == EntityType.RABBIT || aoq2 == EntityType.FOX;
        });
        PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39);
    }
    
    class WolfAvoidEntityGoal<T extends LivingEntity> extends AvoidEntityGoal<T> {
        private final Wolf wolf;
        
        public WolfAvoidEntityGoal(final Wolf azk2, final Class<T> class3, final float float4, final double double5, final double double6) {
            super(azk2, class3, float4, double5, double6);
            this.wolf = azk2;
        }
        
        @Override
        public boolean canUse() {
            return super.canUse() && this.toAvoid instanceof Llama && !this.wolf.isTame() && this.avoidLlama((Llama)this.toAvoid);
        }
        
        private boolean avoidLlama(final Llama azp) {
            return azp.getStrength() >= Wolf.this.random.nextInt(5);
        }
        
        @Override
        public void start() {
            Wolf.this.setTarget(null);
            super.start();
        }
        
        @Override
        public void tick() {
            Wolf.this.setTarget(null);
            super.tick();
        }
    }
}
