package net.minecraft.world.level.block;

import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.tags.Tag;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import com.google.common.cache.LoadingCache;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.pattern.BlockPattern;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvents;
import javax.annotation.Nullable;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.chat.Component;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.GameRules;
import java.util.Random;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.properties.EnumProperty;

public class NetherPortalBlock extends Block {
    public static final EnumProperty<Direction.Axis> AXIS;
    protected static final VoxelShape X_AXIS_AABB;
    protected static final VoxelShape Z_AXIS_AABB;
    
    public NetherPortalBlock(final Properties c) {
        super(c);
        this.registerDefaultState(((StateHolder<O, BlockState>)this.stateDefinition.any()).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.X));
    }
    
    @Override
    public VoxelShape getShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
        switch (cfj.<Direction.Axis>getValue(NetherPortalBlock.AXIS)) {
            case Z: {
                return NetherPortalBlock.Z_AXIS_AABB;
            }
            default: {
                return NetherPortalBlock.X_AXIS_AABB;
            }
        }
    }
    
    @Override
    public void randomTick(final BlockState cfj, final ServerLevel zd, BlockPos fu, final Random random) {
        if (zd.dimensionType().natural() && zd.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < zd.getDifficulty().getId()) {
            while (zd.getBlockState(fu).is(this)) {
                fu = fu.below();
            }
            if (zd.getBlockState(fu).isValidSpawn(zd, fu, EntityType.ZOMBIFIED_PIGLIN)) {
                final Entity aom6 = EntityType.ZOMBIFIED_PIGLIN.spawn(zd, null, null, null, fu.above(), MobSpawnType.STRUCTURE, false, false);
                if (aom6 != null) {
                    aom6.changingDimensionDelay = aom6.getDimensionChangingDelay();
                }
            }
        }
    }
    
    public static boolean trySpawnPortal(final LevelAccessor bqc, final BlockPos fu) {
        final PortalShape a3 = isPortal(bqc, fu);
        if (a3 != null) {
            a3.createPortalBlocks();
            return true;
        }
        return false;
    }
    
    @Nullable
    public static PortalShape isPortal(final LevelAccessor bqc, final BlockPos fu) {
        final PortalShape a3 = new PortalShape(bqc, fu, Direction.Axis.X);
        if (a3.isValid() && a3.numPortalBlocks == 0) {
            return a3;
        }
        final PortalShape a4 = new PortalShape(bqc, fu, Direction.Axis.Z);
        if (a4.isValid() && a4.numPortalBlocks == 0) {
            return a4;
        }
        return null;
    }
    
    @Override
    public BlockState updateShape(final BlockState cfj1, final Direction fz, final BlockState cfj3, final LevelAccessor bqc, final BlockPos fu5, final BlockPos fu6) {
        final Direction.Axis a8 = fz.getAxis();
        final Direction.Axis a9 = cfj1.<Direction.Axis>getValue(NetherPortalBlock.AXIS);
        final boolean boolean10 = a9 != a8 && a8.isHorizontal();
        if (boolean10 || cfj3.is(this) || new PortalShape(bqc, fu5, a9).isComplete()) {
            return super.updateShape(cfj1, fz, cfj3, bqc, fu5, fu6);
        }
        return Blocks.AIR.defaultBlockState();
    }
    
    @Override
    public void entityInside(final BlockState cfj, final Level bqb, final BlockPos fu, final Entity aom) {
        if (!aom.isPassenger() && !aom.isVehicle() && aom.canChangeDimensions()) {
            aom.handleInsidePortal(fu);
        }
    }
    
    @Override
    public void animateTick(final BlockState cfj, final Level bqb, final BlockPos fu, final Random random) {
        if (random.nextInt(100) == 0) {
            bqb.playLocalSound(fu.getX() + 0.5, fu.getY() + 0.5, fu.getZ() + 0.5, SoundEvents.PORTAL_AMBIENT, SoundSource.BLOCKS, 0.5f, random.nextFloat() * 0.4f + 0.8f, false);
        }
        for (int integer6 = 0; integer6 < 4; ++integer6) {
            double double7 = fu.getX() + random.nextDouble();
            final double double8 = fu.getY() + random.nextDouble();
            double double9 = fu.getZ() + random.nextDouble();
            double double10 = (random.nextFloat() - 0.5) * 0.5;
            final double double11 = (random.nextFloat() - 0.5) * 0.5;
            double double12 = (random.nextFloat() - 0.5) * 0.5;
            final int integer7 = random.nextInt(2) * 2 - 1;
            if (bqb.getBlockState(fu.west()).is(this) || bqb.getBlockState(fu.east()).is(this)) {
                double9 = fu.getZ() + 0.5 + 0.25 * integer7;
                double12 = random.nextFloat() * 2.0f * integer7;
            }
            else {
                double7 = fu.getX() + 0.5 + 0.25 * integer7;
                double10 = random.nextFloat() * 2.0f * integer7;
            }
            bqb.addParticle(ParticleTypes.PORTAL, double7, double8, double9, double10, double11, double12);
        }
    }
    
    @Override
    public ItemStack getCloneItemStack(final BlockGetter bpg, final BlockPos fu, final BlockState cfj) {
        return ItemStack.EMPTY;
    }
    
    @Override
    public BlockState rotate(final BlockState cfj, final Rotation cap) {
        switch (cap) {
            case COUNTERCLOCKWISE_90:
            case CLOCKWISE_90: {
                switch (cfj.<Direction.Axis>getValue(NetherPortalBlock.AXIS)) {
                    case X: {
                        return ((StateHolder<O, BlockState>)cfj).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.Z);
                    }
                    case Z: {
                        return ((StateHolder<O, BlockState>)cfj).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.X);
                    }
                    default: {
                        return cfj;
                    }
                }
                break;
            }
            default: {
                return cfj;
            }
        }
    }
    
    @Override
    protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> a) {
        a.add(NetherPortalBlock.AXIS);
    }
    
    public static BlockPattern.BlockPatternMatch getPortalShape(final LevelAccessor bqc, final BlockPos fu) {
        Direction.Axis a3 = Direction.Axis.Z;
        PortalShape a4 = new PortalShape(bqc, fu, Direction.Axis.X);
        final LoadingCache<BlockPos, BlockInWorld> loadingCache5 = BlockPattern.createLevelCache(bqc, true);
        if (!a4.isValid()) {
            a3 = Direction.Axis.X;
            a4 = new PortalShape(bqc, fu, Direction.Axis.Z);
        }
        if (!a4.isValid()) {
            return new BlockPattern.BlockPatternMatch(fu, Direction.NORTH, Direction.UP, loadingCache5, 1, 1, 1);
        }
        final int[] arr6 = new int[Direction.AxisDirection.values().length];
        final Direction fz7 = a4.rightDir.getCounterClockWise();
        final BlockPos fu2 = a4.bottomLeft.above(a4.getHeight() - 1);
        for (final Direction.AxisDirection b12 : Direction.AxisDirection.values()) {
            final BlockPattern.BlockPatternMatch b13 = new BlockPattern.BlockPatternMatch((fz7.getAxisDirection() == b12) ? fu2 : fu2.relative(a4.rightDir, a4.getWidth() - 1), Direction.get(b12, a3), Direction.UP, loadingCache5, a4.getWidth(), a4.getHeight(), 1);
            for (int integer14 = 0; integer14 < a4.getWidth(); ++integer14) {
                for (int integer15 = 0; integer15 < a4.getHeight(); ++integer15) {
                    final BlockInWorld cfn16 = b13.getBlock(integer14, integer15, 1);
                    if (!cfn16.getState().isAir()) {
                        final int[] array = arr6;
                        final int ordinal = b12.ordinal();
                        ++array[ordinal];
                    }
                }
            }
        }
        Direction.AxisDirection b14 = Direction.AxisDirection.POSITIVE;
        for (final Direction.AxisDirection b15 : Direction.AxisDirection.values()) {
            if (arr6[b15.ordinal()] < arr6[b14.ordinal()]) {
                b14 = b15;
            }
        }
        return new BlockPattern.BlockPatternMatch((fz7.getAxisDirection() == b14) ? fu2 : fu2.relative(a4.rightDir, a4.getWidth() - 1), Direction.get(b14, a3), Direction.UP, loadingCache5, a4.getWidth(), a4.getHeight(), 1);
    }
    
    static {
        AXIS = BlockStateProperties.HORIZONTAL_AXIS;
        X_AXIS_AABB = Block.box(0.0, 0.0, 6.0, 16.0, 16.0, 10.0);
        Z_AXIS_AABB = Block.box(6.0, 0.0, 0.0, 10.0, 16.0, 16.0);
    }
    
    public static class PortalShape {
        private final LevelAccessor level;
        private final Direction.Axis axis;
        private final Direction rightDir;
        private final Direction leftDir;
        private int numPortalBlocks;
        @Nullable
        private BlockPos bottomLeft;
        private int height;
        private int width;
        
        public PortalShape(final LevelAccessor bqc, BlockPos fu, final Direction.Axis a) {
            this.level = bqc;
            this.axis = a;
            if (a == Direction.Axis.X) {
                this.leftDir = Direction.EAST;
                this.rightDir = Direction.WEST;
            }
            else {
                this.leftDir = Direction.NORTH;
                this.rightDir = Direction.SOUTH;
            }
            for (BlockPos fu2 = fu; fu.getY() > fu2.getY() - 21 && fu.getY() > 0 && this.isEmpty(bqc.getBlockState(fu.below())); fu = fu.below()) {}
            final int integer6 = this.getDistanceUntilEdge(fu, this.leftDir) - 1;
            if (integer6 >= 0) {
                this.bottomLeft = fu.relative(this.leftDir, integer6);
                this.width = this.getDistanceUntilEdge(this.bottomLeft, this.rightDir);
                if (this.width < 2 || this.width > 21) {
                    this.bottomLeft = null;
                    this.width = 0;
                }
            }
            if (this.bottomLeft != null) {
                this.height = this.calculatePortalHeight();
            }
        }
        
        protected int getDistanceUntilEdge(final BlockPos fu, final Direction fz) {
            int integer4;
            for (integer4 = 0; integer4 < 22; ++integer4) {
                final BlockPos fu2 = fu.relative(fz, integer4);
                if (!this.isEmpty(this.level.getBlockState(fu2))) {
                    break;
                }
                if (!this.level.getBlockState(fu2.below()).is(Blocks.OBSIDIAN)) {
                    break;
                }
            }
            if (this.level.getBlockState(fu.relative(fz, integer4)).is(Blocks.OBSIDIAN)) {
                return integer4;
            }
            return 0;
        }
        
        public int getHeight() {
            return this.height;
        }
        
        public int getWidth() {
            return this.width;
        }
        
        protected int calculatePortalHeight() {
            this.height = 0;
        Label_0177:
            while (this.height < 21) {
                for (int integer2 = 0; integer2 < this.width; ++integer2) {
                    final BlockPos fu3 = this.bottomLeft.relative(this.rightDir, integer2).above(this.height);
                    final BlockState cfj4 = this.level.getBlockState(fu3);
                    if (!this.isEmpty(cfj4)) {
                        break Label_0177;
                    }
                    if (cfj4.is(Blocks.NETHER_PORTAL)) {
                        ++this.numPortalBlocks;
                    }
                    if (integer2 == 0) {
                        if (!this.level.getBlockState(fu3.relative(this.leftDir)).is(Blocks.OBSIDIAN)) {
                            break Label_0177;
                        }
                    }
                    else if (integer2 == this.width - 1 && !this.level.getBlockState(fu3.relative(this.rightDir)).is(Blocks.OBSIDIAN)) {
                        break Label_0177;
                    }
                }
                ++this.height;
            }
            for (int integer2 = 0; integer2 < this.width; ++integer2) {
                if (!this.level.getBlockState(this.bottomLeft.relative(this.rightDir, integer2).above(this.height)).is(Blocks.OBSIDIAN)) {
                    this.height = 0;
                    break;
                }
            }
            if (this.height > 21 || this.height < 3) {
                this.bottomLeft = null;
                this.width = 0;
                return this.height = 0;
            }
            return this.height;
        }
        
        protected boolean isEmpty(final BlockState cfj) {
            return cfj.isAir() || cfj.is(BlockTags.FIRE) || cfj.is(Blocks.NETHER_PORTAL);
        }
        
        public boolean isValid() {
            return this.bottomLeft != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
        }
        
        public void createPortalBlocks() {
            for (int integer2 = 0; integer2 < this.width; ++integer2) {
                final BlockPos fu3 = this.bottomLeft.relative(this.rightDir, integer2);
                for (int integer3 = 0; integer3 < this.height; ++integer3) {
                    this.level.setBlock(fu3.above(integer3), ((StateHolder<O, BlockState>)Blocks.NETHER_PORTAL.defaultBlockState()).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, this.axis), 18);
                }
            }
        }
        
        private boolean hasAllPortalBlocks() {
            return this.numPortalBlocks >= this.width * this.height;
        }
        
        public boolean isComplete() {
            return this.isValid() && this.hasAllPortalBlocks();
        }
    }
}
