package net.minecraft.world.level.pathfinder;

import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.Block;
import net.minecraft.tags.Tag;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BaseRailBlock;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.PathNavigationRegion;

public class TurtleNodeEvaluator extends WalkNodeEvaluator {
    private float oldWalkableCost;
    private float oldWaterBorderCost;
    
    @Override
    public void prepare(final PathNavigationRegion bql, final Mob aoz) {
        super.prepare(bql, aoz);
        aoz.setPathfindingMalus(BlockPathTypes.WATER, 0.0f);
        this.oldWalkableCost = aoz.getPathfindingMalus(BlockPathTypes.WALKABLE);
        aoz.setPathfindingMalus(BlockPathTypes.WALKABLE, 6.0f);
        this.oldWaterBorderCost = aoz.getPathfindingMalus(BlockPathTypes.WATER_BORDER);
        aoz.setPathfindingMalus(BlockPathTypes.WATER_BORDER, 4.0f);
    }
    
    @Override
    public void done() {
        this.mob.setPathfindingMalus(BlockPathTypes.WALKABLE, this.oldWalkableCost);
        this.mob.setPathfindingMalus(BlockPathTypes.WATER_BORDER, this.oldWaterBorderCost);
        super.done();
    }
    
    @Override
    public Node getStart() {
        return this.getNode(Mth.floor(this.mob.getBoundingBox().minX), Mth.floor(this.mob.getBoundingBox().minY + 0.5), Mth.floor(this.mob.getBoundingBox().minZ));
    }
    
    @Override
    public Target getGoal(final double double1, final double double2, final double double3) {
        return new Target(this.getNode(Mth.floor(double1), Mth.floor(double2 + 0.5), Mth.floor(double3)));
    }
    
    @Override
    public int getNeighbors(final Node[] arr, final Node czd) {
        int integer4 = 0;
        final int integer5 = 1;
        final BlockPos fu6 = new BlockPos(czd.x, czd.y, czd.z);
        final double double7 = this.inWaterDependentPosHeight(fu6);
        final Node czd2 = this.getAcceptedNode(czd.x, czd.y, czd.z + 1, 1, double7);
        final Node czd3 = this.getAcceptedNode(czd.x - 1, czd.y, czd.z, 1, double7);
        final Node czd4 = this.getAcceptedNode(czd.x + 1, czd.y, czd.z, 1, double7);
        final Node czd5 = this.getAcceptedNode(czd.x, czd.y, czd.z - 1, 1, double7);
        final Node czd6 = this.getAcceptedNode(czd.x, czd.y + 1, czd.z, 0, double7);
        final Node czd7 = this.getAcceptedNode(czd.x, czd.y - 1, czd.z, 1, double7);
        if (czd2 != null && !czd2.closed) {
            arr[integer4++] = czd2;
        }
        if (czd3 != null && !czd3.closed) {
            arr[integer4++] = czd3;
        }
        if (czd4 != null && !czd4.closed) {
            arr[integer4++] = czd4;
        }
        if (czd5 != null && !czd5.closed) {
            arr[integer4++] = czd5;
        }
        if (czd6 != null && !czd6.closed) {
            arr[integer4++] = czd6;
        }
        if (czd7 != null && !czd7.closed) {
            arr[integer4++] = czd7;
        }
        final boolean boolean15 = czd5 == null || czd5.type == BlockPathTypes.OPEN || czd5.costMalus != 0.0f;
        final boolean boolean16 = czd2 == null || czd2.type == BlockPathTypes.OPEN || czd2.costMalus != 0.0f;
        final boolean boolean17 = czd4 == null || czd4.type == BlockPathTypes.OPEN || czd4.costMalus != 0.0f;
        final boolean boolean18 = czd3 == null || czd3.type == BlockPathTypes.OPEN || czd3.costMalus != 0.0f;
        if (boolean15 && boolean18) {
            final Node czd8 = this.getAcceptedNode(czd.x - 1, czd.y, czd.z - 1, 1, double7);
            if (czd8 != null && !czd8.closed) {
                arr[integer4++] = czd8;
            }
        }
        if (boolean15 && boolean17) {
            final Node czd8 = this.getAcceptedNode(czd.x + 1, czd.y, czd.z - 1, 1, double7);
            if (czd8 != null && !czd8.closed) {
                arr[integer4++] = czd8;
            }
        }
        if (boolean16 && boolean18) {
            final Node czd8 = this.getAcceptedNode(czd.x - 1, czd.y, czd.z + 1, 1, double7);
            if (czd8 != null && !czd8.closed) {
                arr[integer4++] = czd8;
            }
        }
        if (boolean16 && boolean17) {
            final Node czd8 = this.getAcceptedNode(czd.x + 1, czd.y, czd.z + 1, 1, double7);
            if (czd8 != null && !czd8.closed) {
                arr[integer4++] = czd8;
            }
        }
        return integer4;
    }
    
    private double inWaterDependentPosHeight(final BlockPos fu) {
        if (!this.mob.isInWater()) {
            final BlockPos fu2 = fu.below();
            final VoxelShape dfg4 = this.level.getBlockState(fu2).getCollisionShape(this.level, fu2);
            return fu2.getY() + (dfg4.isEmpty() ? 0.0 : dfg4.max(Direction.Axis.Y));
        }
        return fu.getY() + 0.5;
    }
    
    @Nullable
    private Node getAcceptedNode(final int integer1, int integer2, final int integer3, final int integer4, final double double5) {
        Node czd8 = null;
        final BlockPos fu9 = new BlockPos(integer1, integer2, integer3);
        final double double6 = this.inWaterDependentPosHeight(fu9);
        if (double6 - double5 > 1.125) {
            return null;
        }
        BlockPathTypes czb12 = this.getBlockPathType(this.level, integer1, integer2, integer3, this.mob, this.entityWidth, this.entityHeight, this.entityDepth, false, false);
        float float13 = this.mob.getPathfindingMalus(czb12);
        final double double7 = this.mob.getBbWidth() / 2.0;
        if (float13 >= 0.0f) {
            czd8 = this.getNode(integer1, integer2, integer3);
            czd8.type = czb12;
            czd8.costMalus = Math.max(czd8.costMalus, float13);
        }
        if (czb12 == BlockPathTypes.WATER || czb12 == BlockPathTypes.WALKABLE) {
            if (integer2 < this.mob.level.getSeaLevel() - 10 && czd8 != null) {
                final Node node = czd8;
                ++node.costMalus;
            }
            return czd8;
        }
        if (czd8 == null && integer4 > 0 && czb12 != BlockPathTypes.FENCE && czb12 != BlockPathTypes.UNPASSABLE_RAIL && czb12 != BlockPathTypes.TRAPDOOR) {
            czd8 = this.getAcceptedNode(integer1, integer2 + 1, integer3, integer4 - 1, double5);
        }
        if (czb12 == BlockPathTypes.OPEN) {
            final AABB deg16 = 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.mob.level.noCollision(this.mob, deg16)) {
                return null;
            }
            final BlockPathTypes czb13 = this.getBlockPathType(this.level, integer1, integer2 - 1, integer3, this.mob, this.entityWidth, this.entityHeight, this.entityDepth, false, false);
            if (czb13 == BlockPathTypes.BLOCKED) {
                czd8 = this.getNode(integer1, integer2, integer3);
                czd8.type = BlockPathTypes.WALKABLE;
                czd8.costMalus = Math.max(czd8.costMalus, float13);
                return czd8;
            }
            if (czb13 == BlockPathTypes.WATER) {
                czd8 = this.getNode(integer1, integer2, integer3);
                czd8.type = BlockPathTypes.WATER;
                czd8.costMalus = Math.max(czd8.costMalus, float13);
                return czd8;
            }
            int integer5 = 0;
            while (integer2 > 0 && czb12 == BlockPathTypes.OPEN) {
                --integer2;
                if (integer5++ >= this.mob.getMaxFallDistance()) {
                    return null;
                }
                czb12 = this.getBlockPathType(this.level, integer1, integer2, integer3, this.mob, this.entityWidth, this.entityHeight, this.entityDepth, false, false);
                float13 = this.mob.getPathfindingMalus(czb12);
                if (czb12 != BlockPathTypes.OPEN && float13 >= 0.0f) {
                    czd8 = this.getNode(integer1, integer2, integer3);
                    czd8.type = czb12;
                    czd8.costMalus = Math.max(czd8.costMalus, float13);
                    break;
                }
                if (float13 < 0.0f) {
                    return null;
                }
            }
        }
        return czd8;
    }
    
    @Override
    protected BlockPathTypes evaluateBlockPathType(final BlockGetter bpg, final boolean boolean2, final boolean boolean3, final BlockPos fu, BlockPathTypes czb) {
        if (czb == BlockPathTypes.RAIL && !(bpg.getBlockState(fu).getBlock() instanceof BaseRailBlock) && !(bpg.getBlockState(fu.below()).getBlock() instanceof BaseRailBlock)) {
            czb = BlockPathTypes.UNPASSABLE_RAIL;
        }
        if (czb == BlockPathTypes.DOOR_OPEN || czb == BlockPathTypes.DOOR_WOOD_CLOSED || czb == BlockPathTypes.DOOR_IRON_CLOSED) {
            czb = BlockPathTypes.BLOCKED;
        }
        if (czb == BlockPathTypes.LEAVES) {
            czb = BlockPathTypes.BLOCKED;
        }
        return czb;
    }
    
    @Override
    public BlockPathTypes getBlockPathType(final BlockGetter bpg, final int integer2, final int integer3, final int integer4) {
        final BlockPos.MutableBlockPos a6 = new BlockPos.MutableBlockPos();
        BlockPathTypes czb7 = WalkNodeEvaluator.getBlockPathTypeRaw(bpg, a6.set(integer2, integer3, integer4));
        if (czb7 == BlockPathTypes.WATER) {
            for (final Direction fz11 : Direction.values()) {
                final BlockPathTypes czb8 = WalkNodeEvaluator.getBlockPathTypeRaw(bpg, a6.set(integer2, integer3, integer4).move(fz11));
                if (czb8 == BlockPathTypes.BLOCKED) {
                    return BlockPathTypes.WATER_BORDER;
                }
            }
            return BlockPathTypes.WATER;
        }
        if (czb7 == BlockPathTypes.OPEN && integer3 >= 1) {
            final BlockState cfj8 = bpg.getBlockState(new BlockPos(integer2, integer3 - 1, integer4));
            final BlockPathTypes czb9 = WalkNodeEvaluator.getBlockPathTypeRaw(bpg, a6.set(integer2, integer3 - 1, integer4));
            if (czb9 == BlockPathTypes.WALKABLE || czb9 == BlockPathTypes.OPEN || czb9 == BlockPathTypes.LAVA) {
                czb7 = BlockPathTypes.OPEN;
            }
            else {
                czb7 = BlockPathTypes.WALKABLE;
            }
            if (czb9 == BlockPathTypes.DAMAGE_FIRE || cfj8.is(Blocks.MAGMA_BLOCK) || cfj8.is(BlockTags.CAMPFIRES)) {
                czb7 = BlockPathTypes.DAMAGE_FIRE;
            }
            if (czb9 == BlockPathTypes.DAMAGE_CACTUS) {
                czb7 = BlockPathTypes.DAMAGE_CACTUS;
            }
            if (czb9 == BlockPathTypes.DAMAGE_OTHER) {
                czb7 = BlockPathTypes.DAMAGE_OTHER;
            }
        }
        if (czb7 == BlockPathTypes.WALKABLE) {
            czb7 = WalkNodeEvaluator.checkNeighbourBlocks(bpg, a6.set(integer2, integer3, integer4), czb7);
        }
        return czb7;
    }
}
