package net.minecraft.world.level.block;

import net.minecraft.world.level.block.state.AbstractStateHolder;
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.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(((AbstractStateHolder<O, BlockState>)this.stateDefinition.any()).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.X));
    }
    
    @Override
    public VoxelShape getShape(final BlockState byj, final BlockGetter bjg, final BlockPos fk, final CollisionContext cvq) {
        switch (byj.<Direction.Axis>getValue(NetherPortalBlock.AXIS)) {
            case Z: {
                return NetherPortalBlock.Z_AXIS_AABB;
            }
            default: {
                return NetherPortalBlock.X_AXIS_AABB;
            }
        }
    }
    
    @Override
    public void tick(final BlockState byj, final ServerLevel xd, BlockPos fk, final Random random) {
        if (xd.dimension.isNaturalDimension() && xd.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < xd.getDifficulty().getId()) {
            while (xd.getBlockState(fk).getBlock() == this) {
                fk = fk.below();
            }
            if (xd.getBlockState(fk).isValidSpawn(xd, fk, EntityType.ZOMBIE_PIGMAN)) {
                final Entity akq6 = EntityType.ZOMBIE_PIGMAN.spawn(xd, null, null, null, fk.above(), MobSpawnType.STRUCTURE, false, false);
                if (akq6 != null) {
                    akq6.changingDimensionDelay = akq6.getDimensionChangingDelay();
                }
            }
        }
    }
    
    public boolean trySpawnPortal(final LevelAccessor bjx, final BlockPos fk) {
        final PortalShape a4 = this.isPortal(bjx, fk);
        if (a4 != null) {
            a4.createPortalBlocks();
            return true;
        }
        return false;
    }
    
    @Nullable
    public PortalShape isPortal(final LevelAccessor bjx, final BlockPos fk) {
        final PortalShape a4 = new PortalShape(bjx, fk, Direction.Axis.X);
        if (a4.isValid() && a4.numPortalBlocks == 0) {
            return a4;
        }
        final PortalShape a5 = new PortalShape(bjx, fk, Direction.Axis.Z);
        if (a5.isValid() && a5.numPortalBlocks == 0) {
            return a5;
        }
        return null;
    }
    
    @Override
    public BlockState updateShape(final BlockState byj1, final Direction fp, final BlockState byj3, final LevelAccessor bjx, final BlockPos fk5, final BlockPos fk6) {
        final Direction.Axis a8 = fp.getAxis();
        final Direction.Axis a9 = byj1.<Direction.Axis>getValue(NetherPortalBlock.AXIS);
        final boolean boolean10 = a9 != a8 && a8.isHorizontal();
        if (boolean10 || byj3.getBlock() == this || new PortalShape(bjx, fk5, a9).isComplete()) {
            return super.updateShape(byj1, fp, byj3, bjx, fk5, fk6);
        }
        return Blocks.AIR.defaultBlockState();
    }
    
    @Override
    public void entityInside(final BlockState byj, final Level bjw, final BlockPos fk, final Entity akq) {
        if (!akq.isPassenger() && !akq.isVehicle() && akq.canChangeDimensions()) {
            akq.handleInsidePortal(fk);
        }
    }
    
    @Override
    public void animateTick(final BlockState byj, final Level bjw, final BlockPos fk, final Random random) {
        if (random.nextInt(100) == 0) {
            bjw.playLocalSound(fk.getX() + 0.5, fk.getY() + 0.5, fk.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 = fk.getX() + (double)random.nextFloat();
            final double double8 = fk.getY() + (double)random.nextFloat();
            double double9 = fk.getZ() + (double)random.nextFloat();
            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 (bjw.getBlockState(fk.west()).getBlock() == this || bjw.getBlockState(fk.east()).getBlock() == this) {
                double9 = fk.getZ() + 0.5 + 0.25 * integer7;
                double12 = random.nextFloat() * 2.0f * integer7;
            }
            else {
                double7 = fk.getX() + 0.5 + 0.25 * integer7;
                double10 = random.nextFloat() * 2.0f * integer7;
            }
            bjw.addParticle(ParticleTypes.PORTAL, double7, double8, double9, double10, double11, double12);
        }
    }
    
    @Override
    public ItemStack getCloneItemStack(final BlockGetter bjg, final BlockPos fk, final BlockState byj) {
        return ItemStack.EMPTY;
    }
    
    @Override
    public BlockState rotate(final BlockState byj, final Rotation btu) {
        switch (btu) {
            case COUNTERCLOCKWISE_90:
            case CLOCKWISE_90: {
                switch (byj.<Direction.Axis>getValue(NetherPortalBlock.AXIS)) {
                    case X: {
                        return ((AbstractStateHolder<O, BlockState>)byj).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.Z);
                    }
                    case Z: {
                        return ((AbstractStateHolder<O, BlockState>)byj).<Direction.Axis, Direction.Axis>setValue(NetherPortalBlock.AXIS, Direction.Axis.X);
                    }
                    default: {
                        return byj;
                    }
                }
                break;
            }
            default: {
                return byj;
            }
        }
    }
    
    @Override
    protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> a) {
        a.add(NetherPortalBlock.AXIS);
    }
    
    public static BlockPattern.BlockPatternMatch getPortalShape(final LevelAccessor bjx, final BlockPos fk) {
        Direction.Axis a3 = Direction.Axis.Z;
        PortalShape a4 = new PortalShape(bjx, fk, Direction.Axis.X);
        final LoadingCache<BlockPos, BlockInWorld> loadingCache5 = BlockPattern.createLevelCache(bjx, true);
        if (!a4.isValid()) {
            a3 = Direction.Axis.X;
            a4 = new PortalShape(bjx, fk, Direction.Axis.Z);
        }
        if (!a4.isValid()) {
            return new BlockPattern.BlockPatternMatch(fk, Direction.NORTH, Direction.UP, loadingCache5, 1, 1, 1);
        }
        final int[] arr6 = new int[Direction.AxisDirection.values().length];
        final Direction fp7 = a4.rightDir.getCounterClockWise();
        final BlockPos fk2 = a4.bottomLeft.above(a4.getHeight() - 1);
        for (final Direction.AxisDirection b12 : Direction.AxisDirection.values()) {
            final BlockPattern.BlockPatternMatch b13 = new BlockPattern.BlockPatternMatch((fp7.getAxisDirection() == b12) ? fk2 : fk2.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 byn16 = b13.getBlock(integer14, integer15, 1);
                    if (!byn16.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((fp7.getAxisDirection() == b14) ? fk2 : fk2.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 bjx, BlockPos fk, final Direction.Axis a) {
            this.level = bjx;
            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 fk2 = fk; fk.getY() > fk2.getY() - 21 && fk.getY() > 0 && this.isEmpty(bjx.getBlockState(fk.below())); fk = fk.below()) {}
            final int integer6 = this.getDistanceUntilEdge(fk, this.leftDir) - 1;
            if (integer6 >= 0) {
                this.bottomLeft = fk.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 fk, final Direction fp) {
            int integer4;
            for (integer4 = 0; integer4 < 22; ++integer4) {
                final BlockPos fk2 = fk.relative(fp, integer4);
                if (!this.isEmpty(this.level.getBlockState(fk2))) {
                    break;
                }
                if (this.level.getBlockState(fk2.below()).getBlock() != Blocks.OBSIDIAN) {
                    break;
                }
            }
            final Block bph5 = this.level.getBlockState(fk.relative(fp, integer4)).getBlock();
            if (bph5 == 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_0189:
            while (this.height < 21) {
                for (int integer2 = 0; integer2 < this.width; ++integer2) {
                    final BlockPos fk3 = this.bottomLeft.relative(this.rightDir, integer2).above(this.height);
                    final BlockState byj4 = this.level.getBlockState(fk3);
                    if (!this.isEmpty(byj4)) {
                        break Label_0189;
                    }
                    Block bph5 = byj4.getBlock();
                    if (bph5 == Blocks.NETHER_PORTAL) {
                        ++this.numPortalBlocks;
                    }
                    if (integer2 == 0) {
                        bph5 = this.level.getBlockState(fk3.relative(this.leftDir)).getBlock();
                        if (bph5 != Blocks.OBSIDIAN) {
                            break Label_0189;
                        }
                    }
                    else if (integer2 == this.width - 1) {
                        bph5 = this.level.getBlockState(fk3.relative(this.rightDir)).getBlock();
                        if (bph5 != Blocks.OBSIDIAN) {
                            break Label_0189;
                        }
                    }
                }
                ++this.height;
            }
            for (int integer2 = 0; integer2 < this.width; ++integer2) {
                if (this.level.getBlockState(this.bottomLeft.relative(this.rightDir, integer2).above(this.height)).getBlock() != 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 byj) {
            final Block bph3 = byj.getBlock();
            return byj.isAir() || bph3 == Blocks.FIRE || bph3 == 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 fk3 = this.bottomLeft.relative(this.rightDir, integer2);
                for (int integer3 = 0; integer3 < this.height; ++integer3) {
                    this.level.setBlock(fk3.above(integer3), ((AbstractStateHolder<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();
        }
    }
}
