package net.minecraft.world.level.pathfinder;

import net.minecraft.world.level.material.FluidState;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BaseRailBlock;
import java.util.EnumSet;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import javax.annotation.Nullable;
import net.minecraft.core.Direction;
import java.util.Iterator;
import java.util.Set;
import net.minecraft.world.level.block.state.BlockState;
import com.google.common.collect.Sets;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.PathNavigationRegion;

public class WalkNodeEvaluator extends NodeEvaluator {
    protected float oldWaterCost;
    
    @Override
    public void prepare(final PathNavigationRegion bkh, final Mob ala) {
        super.prepare(bkh, ala);
        this.oldWaterCost = ala.getPathfindingMalus(BlockPathTypes.WATER);
    }
    
    @Override
    public void done() {
        this.mob.setPathfindingMalus(BlockPathTypes.WATER, this.oldWaterCost);
        super.done();
    }
    
    @Override
    public Node getStart() {
        int integer2;
        if (this.canFloat() && this.mob.isInWater()) {
            integer2 = Mth.floor(this.mob.getY());
            final BlockPos.MutableBlockPos a3 = new BlockPos.MutableBlockPos(this.mob.getX(), integer2, this.mob.getZ());
            for (BlockState byj4 = this.level.getBlockState(a3); byj4.getBlock() == Blocks.WATER || byj4.getFluidState() == Fluids.WATER.getSource(false); byj4 = this.level.getBlockState(a3)) {
                ++integer2;
                a3.set(this.mob.getX(), integer2, this.mob.getZ());
            }
            --integer2;
        }
        else if (this.mob.onGround) {
            integer2 = Mth.floor(this.mob.getY() + 0.5);
        }
        else {
            BlockPos fk3;
            for (fk3 = new BlockPos(this.mob); (this.level.getBlockState(fk3).isAir() || this.level.getBlockState(fk3).isPathfindable(this.level, fk3, PathComputationType.LAND)) && fk3.getY() > 0; fk3 = fk3.below()) {}
            integer2 = fk3.above().getY();
        }
        BlockPos fk3 = new BlockPos(this.mob);
        final BlockPathTypes cqm4 = this.getBlockPathType(this.mob, fk3.getX(), integer2, fk3.getZ());
        if (this.mob.getPathfindingMalus(cqm4) < 0.0f) {
            final Set<BlockPos> set5 = Sets.newHashSet();
            set5.add(new BlockPos(this.mob.getBoundingBox().minX, integer2, this.mob.getBoundingBox().minZ));
            set5.add(new BlockPos(this.mob.getBoundingBox().minX, integer2, this.mob.getBoundingBox().maxZ));
            set5.add(new BlockPos(this.mob.getBoundingBox().maxX, integer2, this.mob.getBoundingBox().minZ));
            set5.add(new BlockPos(this.mob.getBoundingBox().maxX, integer2, this.mob.getBoundingBox().maxZ));
            for (final BlockPos fk4 : set5) {
                final BlockPathTypes cqm5 = this.getBlockPathType(this.mob, fk4);
                if (this.mob.getPathfindingMalus(cqm5) >= 0.0f) {
                    return this.getNode(fk4.getX(), fk4.getY(), fk4.getZ());
                }
            }
        }
        return this.getNode(fk3.getX(), integer2, fk3.getZ());
    }
    
    @Override
    public Target getGoal(final double double1, final double double2, final double double3) {
        return new Target(this.getNode(Mth.floor(double1), Mth.floor(double2), Mth.floor(double3)));
    }
    
    @Override
    public int getNeighbors(final Node[] arr, final Node cqo) {
        int integer4 = 0;
        int integer5 = 0;
        final BlockPathTypes cqm6 = this.getBlockPathType(this.mob, cqo.x, cqo.y + 1, cqo.z);
        if (this.mob.getPathfindingMalus(cqm6) >= 0.0f) {
            final BlockPathTypes cqm7 = this.getBlockPathType(this.mob, cqo.x, cqo.y, cqo.z);
            if (cqm7 == BlockPathTypes.STICKY_HONEY) {
                integer5 = 0;
            }
            else {
                integer5 = Mth.floor(Math.max(1.0f, this.mob.maxUpStep));
            }
        }
        final double double7 = getFloorLevel(this.level, new BlockPos(cqo.x, cqo.y, cqo.z));
        final Node cqo2 = this.getLandNode(cqo.x, cqo.y, cqo.z + 1, integer5, double7, Direction.SOUTH);
        if (cqo2 != null && !cqo2.closed && cqo2.costMalus >= 0.0f) {
            arr[integer4++] = cqo2;
        }
        final Node cqo3 = this.getLandNode(cqo.x - 1, cqo.y, cqo.z, integer5, double7, Direction.WEST);
        if (cqo3 != null && !cqo3.closed && cqo3.costMalus >= 0.0f) {
            arr[integer4++] = cqo3;
        }
        final Node cqo4 = this.getLandNode(cqo.x + 1, cqo.y, cqo.z, integer5, double7, Direction.EAST);
        if (cqo4 != null && !cqo4.closed && cqo4.costMalus >= 0.0f) {
            arr[integer4++] = cqo4;
        }
        final Node cqo5 = this.getLandNode(cqo.x, cqo.y, cqo.z - 1, integer5, double7, Direction.NORTH);
        if (cqo5 != null && !cqo5.closed && cqo5.costMalus >= 0.0f) {
            arr[integer4++] = cqo5;
        }
        final Node cqo6 = this.getLandNode(cqo.x - 1, cqo.y, cqo.z - 1, integer5, double7, Direction.NORTH);
        if (this.isDiagonalValid(cqo, cqo3, cqo5, cqo6)) {
            arr[integer4++] = cqo6;
        }
        final Node cqo7 = this.getLandNode(cqo.x + 1, cqo.y, cqo.z - 1, integer5, double7, Direction.NORTH);
        if (this.isDiagonalValid(cqo, cqo4, cqo5, cqo7)) {
            arr[integer4++] = cqo7;
        }
        final Node cqo8 = this.getLandNode(cqo.x - 1, cqo.y, cqo.z + 1, integer5, double7, Direction.SOUTH);
        if (this.isDiagonalValid(cqo, cqo3, cqo2, cqo8)) {
            arr[integer4++] = cqo8;
        }
        final Node cqo9 = this.getLandNode(cqo.x + 1, cqo.y, cqo.z + 1, integer5, double7, Direction.SOUTH);
        if (this.isDiagonalValid(cqo, cqo4, cqo2, cqo9)) {
            arr[integer4++] = cqo9;
        }
        return integer4;
    }
    
    private boolean isDiagonalValid(final Node cqo1, @Nullable final Node cqo2, @Nullable final Node cqo3, @Nullable final Node cqo4) {
        return cqo4 != null && cqo3 != null && cqo2 != null && !cqo4.closed && cqo3.y <= cqo1.y && cqo2.y <= cqo1.y && cqo4.costMalus >= 0.0f && (cqo3.y < cqo1.y || cqo3.costMalus >= 0.0f) && (cqo2.y < cqo1.y || cqo2.costMalus >= 0.0f);
    }
    
    public static double getFloorLevel(final BlockGetter bjg, final BlockPos fk) {
        final BlockPos fk2 = fk.below();
        final VoxelShape cwf4 = bjg.getBlockState(fk2).getCollisionShape(bjg, fk2);
        return fk2.getY() + (cwf4.isEmpty() ? 0.0 : cwf4.max(Direction.Axis.Y));
    }
    
    @Nullable
    private Node getLandNode(final int integer1, int integer2, final int integer3, final int integer4, final double double5, final Direction fp) {
        Node cqo9 = null;
        final BlockPos fk10 = new BlockPos(integer1, integer2, integer3);
        final double double6 = getFloorLevel(this.level, fk10);
        if (double6 - double5 > 1.125) {
            return null;
        }
        BlockPathTypes cqm13 = this.getBlockPathType(this.mob, integer1, integer2, integer3);
        float float14 = this.mob.getPathfindingMalus(cqm13);
        final double double7 = this.mob.getBbWidth() / 2.0;
        if (float14 >= 0.0f) {
            cqo9 = this.getNode(integer1, integer2, integer3);
            cqo9.type = cqm13;
            cqo9.costMalus = Math.max(cqo9.costMalus, float14);
        }
        if (cqm13 == BlockPathTypes.WALKABLE) {
            return cqo9;
        }
        if ((cqo9 == null || cqo9.costMalus < 0.0f) && integer4 > 0 && cqm13 != BlockPathTypes.FENCE && cqm13 != BlockPathTypes.TRAPDOOR) {
            cqo9 = this.getLandNode(integer1, integer2 + 1, integer3, integer4 - 1, double5, fp);
            if (cqo9 != null && (cqo9.type == BlockPathTypes.OPEN || cqo9.type == BlockPathTypes.WALKABLE) && this.mob.getBbWidth() < 1.0f) {
                final double double8 = integer1 - fp.getStepX() + 0.5;
                final double double9 = integer3 - fp.getStepZ() + 0.5;
                final AABB cvf21 = new AABB(double8 - double7, getFloorLevel(this.level, new BlockPos(double8, integer2 + 1, double9)) + 0.001, double9 - double7, double8 + double7, this.mob.getBbHeight() + getFloorLevel(this.level, new BlockPos(cqo9.x, cqo9.y, cqo9.z)) - 0.002, double9 + double7);
                if (!this.level.noCollision(this.mob, cvf21)) {
                    cqo9 = null;
                }
            }
        }
        if (cqm13 == BlockPathTypes.WATER && !this.canFloat()) {
            if (this.getBlockPathType(this.mob, integer1, integer2 - 1, integer3) != BlockPathTypes.WATER) {
                return cqo9;
            }
            while (integer2 > 0) {
                --integer2;
                cqm13 = this.getBlockPathType(this.mob, integer1, integer2, integer3);
                if (cqm13 != BlockPathTypes.WATER) {
                    return cqo9;
                }
                cqo9 = this.getNode(integer1, integer2, integer3);
                cqo9.type = cqm13;
                cqo9.costMalus = Math.max(cqo9.costMalus, this.mob.getPathfindingMalus(cqm13));
            }
        }
        if (cqm13 == BlockPathTypes.OPEN) {
            final AABB cvf22 = new AABB(integer1 - double7 + 0.5, integer2 + 0.001, integer3 - double7 + 0.5, integer1 + double7 + 0.5, integer2 + this.mob.getBbHeight(), integer3 + double7 + 0.5);
            if (!this.level.noCollision(this.mob, cvf22)) {
                return null;
            }
            if (this.mob.getBbWidth() >= 1.0f) {
                final BlockPathTypes cqm14 = this.getBlockPathType(this.mob, integer1, integer2 - 1, integer3);
                if (cqm14 == BlockPathTypes.BLOCKED) {
                    cqo9 = this.getNode(integer1, integer2, integer3);
                    cqo9.type = BlockPathTypes.WALKABLE;
                    cqo9.costMalus = Math.max(cqo9.costMalus, float14);
                    return cqo9;
                }
            }
            int integer5 = 0;
            final int integer6 = integer2;
            while (cqm13 == BlockPathTypes.OPEN) {
                if (--integer2 < 0) {
                    final Node cqo10 = this.getNode(integer1, integer6, integer3);
                    cqo10.type = BlockPathTypes.BLOCKED;
                    cqo10.costMalus = -1.0f;
                    return cqo10;
                }
                final Node cqo10 = this.getNode(integer1, integer2, integer3);
                if (integer5++ >= this.mob.getMaxFallDistance()) {
                    cqo10.type = BlockPathTypes.BLOCKED;
                    cqo10.costMalus = -1.0f;
                    return cqo10;
                }
                cqm13 = this.getBlockPathType(this.mob, integer1, integer2, integer3);
                float14 = this.mob.getPathfindingMalus(cqm13);
                if (cqm13 != BlockPathTypes.OPEN && float14 >= 0.0f) {
                    cqo9 = cqo10;
                    cqo9.type = cqm13;
                    cqo9.costMalus = Math.max(cqo9.costMalus, float14);
                    break;
                }
                if (float14 < 0.0f) {
                    cqo10.type = BlockPathTypes.BLOCKED;
                    cqo10.costMalus = -1.0f;
                    return cqo10;
                }
            }
        }
        return cqo9;
    }
    
    @Override
    public BlockPathTypes getBlockPathType(final BlockGetter bjg, final int integer2, final int integer3, final int integer4, final Mob ala, final int integer6, final int integer7, final int integer8, final boolean boolean9, final boolean boolean10) {
        final EnumSet<BlockPathTypes> enumSet12 = EnumSet.<BlockPathTypes>noneOf(BlockPathTypes.class);
        BlockPathTypes cqm13 = BlockPathTypes.BLOCKED;
        final double double14 = ala.getBbWidth() / 2.0;
        final BlockPos fk16 = new BlockPos(ala);
        cqm13 = this.getBlockPathTypes(bjg, integer2, integer3, integer4, integer6, integer7, integer8, boolean9, boolean10, enumSet12, cqm13, fk16);
        if (enumSet12.contains(BlockPathTypes.FENCE)) {
            return BlockPathTypes.FENCE;
        }
        BlockPathTypes cqm14 = BlockPathTypes.BLOCKED;
        for (final BlockPathTypes cqm15 : enumSet12) {
            if (ala.getPathfindingMalus(cqm15) < 0.0f) {
                return cqm15;
            }
            if (ala.getPathfindingMalus(cqm15) < ala.getPathfindingMalus(cqm14)) {
                continue;
            }
            cqm14 = cqm15;
        }
        if (cqm13 == BlockPathTypes.OPEN && ala.getPathfindingMalus(cqm14) == 0.0f) {
            return BlockPathTypes.OPEN;
        }
        return cqm14;
    }
    
    public BlockPathTypes getBlockPathTypes(final BlockGetter bjg, final int integer2, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7, final boolean boolean8, final boolean boolean9, final EnumSet<BlockPathTypes> enumSet, BlockPathTypes cqm, final BlockPos fk) {
        for (int integer8 = 0; integer8 < integer5; ++integer8) {
            for (int integer9 = 0; integer9 < integer6; ++integer9) {
                for (int integer10 = 0; integer10 < integer7; ++integer10) {
                    final int integer11 = integer8 + integer2;
                    final int integer12 = integer9 + integer3;
                    final int integer13 = integer10 + integer4;
                    BlockPathTypes cqm2 = this.getBlockPathType(bjg, integer11, integer12, integer13);
                    cqm2 = this.evaluateBlockPathType(bjg, boolean8, boolean9, fk, cqm2);
                    if (integer8 == 0 && integer9 == 0 && integer10 == 0) {
                        cqm = cqm2;
                    }
                    enumSet.add(cqm2);
                }
            }
        }
        return cqm;
    }
    
    protected BlockPathTypes evaluateBlockPathType(final BlockGetter bjg, final boolean boolean2, final boolean boolean3, final BlockPos fk, BlockPathTypes cqm) {
        if (cqm == BlockPathTypes.DOOR_WOOD_CLOSED && boolean2 && boolean3) {
            cqm = BlockPathTypes.WALKABLE;
        }
        if (cqm == BlockPathTypes.DOOR_OPEN && !boolean3) {
            cqm = BlockPathTypes.BLOCKED;
        }
        if (cqm == BlockPathTypes.RAIL && !(bjg.getBlockState(fk).getBlock() instanceof BaseRailBlock) && !(bjg.getBlockState(fk.below()).getBlock() instanceof BaseRailBlock)) {
            cqm = BlockPathTypes.FENCE;
        }
        if (cqm == BlockPathTypes.LEAVES) {
            cqm = BlockPathTypes.BLOCKED;
        }
        return cqm;
    }
    
    private BlockPathTypes getBlockPathType(final Mob ala, final BlockPos fk) {
        return this.getBlockPathType(ala, fk.getX(), fk.getY(), fk.getZ());
    }
    
    private BlockPathTypes getBlockPathType(final Mob ala, final int integer2, final int integer3, final int integer4) {
        return this.getBlockPathType(this.level, integer2, integer3, integer4, ala, this.entityWidth, this.entityHeight, this.entityDepth, this.canOpenDoors(), this.canPassDoors());
    }
    
    @Override
    public BlockPathTypes getBlockPathType(final BlockGetter bjg, final int integer2, final int integer3, final int integer4) {
        return getBlockPathTypeStatic(bjg, integer2, integer3, integer4);
    }
    
    public static BlockPathTypes getBlockPathTypeStatic(final BlockGetter bjg, final int integer2, final int integer3, final int integer4) {
        BlockPathTypes cqm5 = getBlockPathTypeRaw(bjg, integer2, integer3, integer4);
        if (cqm5 == BlockPathTypes.OPEN && integer3 >= 1) {
            final Block bph6 = bjg.getBlockState(new BlockPos(integer2, integer3 - 1, integer4)).getBlock();
            final BlockPathTypes cqm6 = getBlockPathTypeRaw(bjg, integer2, integer3 - 1, integer4);
            cqm5 = ((cqm6 == BlockPathTypes.WALKABLE || cqm6 == BlockPathTypes.OPEN || cqm6 == BlockPathTypes.WATER || cqm6 == BlockPathTypes.LAVA) ? BlockPathTypes.OPEN : BlockPathTypes.WALKABLE);
            if (cqm6 == BlockPathTypes.DAMAGE_FIRE || bph6 == Blocks.MAGMA_BLOCK || bph6 == Blocks.CAMPFIRE) {
                cqm5 = BlockPathTypes.DAMAGE_FIRE;
            }
            if (cqm6 == BlockPathTypes.DAMAGE_CACTUS) {
                cqm5 = BlockPathTypes.DAMAGE_CACTUS;
            }
            if (cqm6 == BlockPathTypes.DAMAGE_OTHER) {
                cqm5 = BlockPathTypes.DAMAGE_OTHER;
            }
            if (cqm6 == BlockPathTypes.STICKY_HONEY) {
                cqm5 = BlockPathTypes.STICKY_HONEY;
            }
        }
        if (cqm5 == BlockPathTypes.WALKABLE) {
            cqm5 = checkNeighbourBlocks(bjg, integer2, integer3, integer4, cqm5);
        }
        return cqm5;
    }
    
    public static BlockPathTypes checkNeighbourBlocks(final BlockGetter bjg, final int integer2, final int integer3, final int integer4, BlockPathTypes cqm) {
        try (final BlockPos.PooledMutableBlockPos b6 = BlockPos.PooledMutableBlockPos.acquire()) {
            for (int integer5 = -1; integer5 <= 1; ++integer5) {
                for (int integer6 = -1; integer6 <= 1; ++integer6) {
                    for (int integer7 = -1; integer7 <= 1; ++integer7) {
                        if (integer5 != 0 || integer7 != 0) {
                            final Block bph11 = bjg.getBlockState(b6.set(integer5 + integer2, integer6 + integer3, integer7 + integer4)).getBlock();
                            if (bph11 == Blocks.CACTUS) {
                                cqm = BlockPathTypes.DANGER_CACTUS;
                            }
                            else if (bph11 == Blocks.FIRE || bph11 == Blocks.LAVA) {
                                cqm = BlockPathTypes.DANGER_FIRE;
                            }
                            else if (bph11 == Blocks.SWEET_BERRY_BUSH) {
                                cqm = BlockPathTypes.DANGER_OTHER;
                            }
                        }
                    }
                }
            }
        }
        return cqm;
    }
    
    protected static BlockPathTypes getBlockPathTypeRaw(final BlockGetter bjg, final int integer2, final int integer3, final int integer4) {
        final BlockPos fk5 = new BlockPos(integer2, integer3, integer4);
        final BlockState byj6 = bjg.getBlockState(fk5);
        final Block bph7 = byj6.getBlock();
        final Material coo8 = byj6.getMaterial();
        if (byj6.isAir()) {
            return BlockPathTypes.OPEN;
        }
        if (bph7.is(BlockTags.TRAPDOORS) || bph7 == Blocks.LILY_PAD) {
            return BlockPathTypes.TRAPDOOR;
        }
        if (bph7 == Blocks.FIRE) {
            return BlockPathTypes.DAMAGE_FIRE;
        }
        if (bph7 == Blocks.CACTUS) {
            return BlockPathTypes.DAMAGE_CACTUS;
        }
        if (bph7 == Blocks.SWEET_BERRY_BUSH) {
            return BlockPathTypes.DAMAGE_OTHER;
        }
        if (bph7 == Blocks.HONEY_BLOCK) {
            return BlockPathTypes.STICKY_HONEY;
        }
        if (bph7 == Blocks.COCOA) {
            return BlockPathTypes.COCOA;
        }
        if (bph7 instanceof DoorBlock && coo8 == Material.WOOD && !byj6.<Boolean>getValue((Property<Boolean>)DoorBlock.OPEN)) {
            return BlockPathTypes.DOOR_WOOD_CLOSED;
        }
        if (bph7 instanceof DoorBlock && coo8 == Material.METAL && !byj6.<Boolean>getValue((Property<Boolean>)DoorBlock.OPEN)) {
            return BlockPathTypes.DOOR_IRON_CLOSED;
        }
        if (bph7 instanceof DoorBlock && byj6.<Boolean>getValue((Property<Boolean>)DoorBlock.OPEN)) {
            return BlockPathTypes.DOOR_OPEN;
        }
        if (bph7 instanceof BaseRailBlock) {
            return BlockPathTypes.RAIL;
        }
        if (bph7 instanceof LeavesBlock) {
            return BlockPathTypes.LEAVES;
        }
        if (bph7.is(BlockTags.FENCES) || bph7.is(BlockTags.WALLS) || (bph7 instanceof FenceGateBlock && !byj6.<Boolean>getValue((Property<Boolean>)FenceGateBlock.OPEN))) {
            return BlockPathTypes.FENCE;
        }
        final FluidState coj9 = bjg.getFluidState(fk5);
        if (coj9.is(FluidTags.WATER)) {
            return BlockPathTypes.WATER;
        }
        if (coj9.is(FluidTags.LAVA)) {
            return BlockPathTypes.LAVA;
        }
        if (byj6.isPathfindable(bjg, fk5, PathComputationType.LAND)) {
            return BlockPathTypes.OPEN;
        }
        return BlockPathTypes.BLOCKED;
    }
}
