package net.minecraft.world.level.block;

import net.minecraft.world.level.block.state.AbstractStateHolder;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.CompoundContainer;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.inventory.AbstractContainerMenu;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.animal.Cat;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.world.level.block.entity.LidBlockEntity;
import java.util.function.BiPredicate;
import net.minecraft.stats.Stats;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.Stat;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.Containers;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.item.BlockPlaceContext;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.function.Supplier;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Container;
import java.util.Optional;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.entity.ChestBlockEntity;

public class ChestBlock extends AbstractChestBlock<ChestBlockEntity> implements SimpleWaterloggedBlock {
    public static final DirectionProperty FACING;
    public static final EnumProperty<ChestType> TYPE;
    public static final BooleanProperty WATERLOGGED;
    protected static final VoxelShape NORTH_AABB;
    protected static final VoxelShape SOUTH_AABB;
    protected static final VoxelShape WEST_AABB;
    protected static final VoxelShape EAST_AABB;
    protected static final VoxelShape AABB;
    private static final DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<Container>> CHEST_COMBINER;
    private static final DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>> MENU_PROVIDER_COMBINER;
    
    protected ChestBlock(final Properties c, final Supplier<BlockEntityType<? extends ChestBlockEntity>> supplier) {
        super(c, supplier);
        this.registerDefaultState(((((AbstractStateHolder<O, BlockState>)this.stateDefinition.any()).setValue((Property<Comparable>)ChestBlock.FACING, Direction.NORTH)).setValue(ChestBlock.TYPE, ChestType.SINGLE)).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, false));
    }
    
    public static DoubleBlockCombiner.BlockType getBlockType(final BlockState byj) {
        final ChestType bzc2 = byj.<ChestType>getValue(ChestBlock.TYPE);
        if (bzc2 == ChestType.SINGLE) {
            return DoubleBlockCombiner.BlockType.SINGLE;
        }
        if (bzc2 == ChestType.RIGHT) {
            return DoubleBlockCombiner.BlockType.FIRST;
        }
        return DoubleBlockCombiner.BlockType.SECOND;
    }
    
    @Override
    public RenderShape getRenderShape(final BlockState byj) {
        return RenderShape.ENTITYBLOCK_ANIMATED;
    }
    
    @Override
    public BlockState updateShape(final BlockState byj1, final Direction fp, final BlockState byj3, final LevelAccessor bjx, final BlockPos fk5, final BlockPos fk6) {
        if (byj1.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
            bjx.getLiquidTicks().scheduleTick(fk5, Fluids.WATER, Fluids.WATER.getTickDelay(bjx));
        }
        if (byj3.getBlock() == this && fp.getAxis().isHorizontal()) {
            final ChestType bzc8 = byj3.<ChestType>getValue(ChestBlock.TYPE);
            if (byj1.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE && bzc8 != ChestType.SINGLE && byj1.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) == byj3.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) && getConnectedDirection(byj3) == fp.getOpposite()) {
                return ((AbstractStateHolder<O, BlockState>)byj1).<ChestType, ChestType>setValue(ChestBlock.TYPE, bzc8.getOpposite());
            }
        }
        else if (getConnectedDirection(byj1) == fp) {
            return ((AbstractStateHolder<O, BlockState>)byj1).<ChestType, ChestType>setValue(ChestBlock.TYPE, ChestType.SINGLE);
        }
        return super.updateShape(byj1, fp, byj3, bjx, fk5, fk6);
    }
    
    @Override
    public VoxelShape getShape(final BlockState byj, final BlockGetter bjg, final BlockPos fk, final CollisionContext cvq) {
        if (byj.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) {
            return ChestBlock.AABB;
        }
        switch (getConnectedDirection(byj)) {
            default: {
                return ChestBlock.NORTH_AABB;
            }
            case SOUTH: {
                return ChestBlock.SOUTH_AABB;
            }
            case WEST: {
                return ChestBlock.WEST_AABB;
            }
            case EAST: {
                return ChestBlock.EAST_AABB;
            }
        }
    }
    
    public static Direction getConnectedDirection(final BlockState byj) {
        final Direction fp2 = byj.<Direction>getValue((Property<Direction>)ChestBlock.FACING);
        return (byj.<ChestType>getValue(ChestBlock.TYPE) == ChestType.LEFT) ? fp2.getClockWise() : fp2.getCounterClockWise();
    }
    
    @Override
    public BlockState getStateForPlacement(final BlockPlaceContext bcq) {
        ChestType bzc3 = ChestType.SINGLE;
        Direction fp4 = bcq.getHorizontalDirection().getOpposite();
        final FluidState coj5 = bcq.getLevel().getFluidState(bcq.getClickedPos());
        final boolean boolean6 = bcq.isSecondaryUseActive();
        final Direction fp5 = bcq.getClickedFace();
        if (fp5.getAxis().isHorizontal() && boolean6) {
            final Direction fp6 = this.candidatePartnerFacing(bcq, fp5.getOpposite());
            if (fp6 != null && fp6.getAxis() != fp5.getAxis()) {
                fp4 = fp6;
                bzc3 = ((fp4.getCounterClockWise() == fp5.getOpposite()) ? ChestType.RIGHT : ChestType.LEFT);
            }
        }
        if (bzc3 == ChestType.SINGLE && !boolean6) {
            if (fp4 == this.candidatePartnerFacing(bcq, fp4.getClockWise())) {
                bzc3 = ChestType.LEFT;
            }
            else if (fp4 == this.candidatePartnerFacing(bcq, fp4.getCounterClockWise())) {
                bzc3 = ChestType.RIGHT;
            }
        }
        return ((((AbstractStateHolder<O, BlockState>)this.defaultBlockState()).setValue((Property<Comparable>)ChestBlock.FACING, fp4)).setValue(ChestBlock.TYPE, bzc3)).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, coj5.getType() == Fluids.WATER);
    }
    
    @Override
    public FluidState getFluidState(final BlockState byj) {
        if (byj.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
            return Fluids.WATER.getSource(false);
        }
        return super.getFluidState(byj);
    }
    
    @Nullable
    private Direction candidatePartnerFacing(final BlockPlaceContext bcq, final Direction fp) {
        final BlockState byj4 = bcq.getLevel().getBlockState(bcq.getClickedPos().relative(fp));
        return (byj4.getBlock() == this && byj4.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) ? byj4.<Direction>getValue((Property<Direction>)ChestBlock.FACING) : null;
    }
    
    @Override
    public void setPlacedBy(final Level bjw, final BlockPos fk, final BlockState byj, final LivingEntity akz, final ItemStack ben) {
        if (ben.hasCustomHoverName()) {
            final BlockEntity bwl7 = bjw.getBlockEntity(fk);
            if (bwl7 instanceof ChestBlockEntity) {
                ((ChestBlockEntity)bwl7).setCustomName(ben.getHoverName());
            }
        }
    }
    
    @Override
    public void onRemove(final BlockState byj1, final Level bjw, final BlockPos fk, final BlockState byj4, final boolean boolean5) {
        if (byj1.getBlock() == byj4.getBlock()) {
            return;
        }
        final BlockEntity bwl7 = bjw.getBlockEntity(fk);
        if (bwl7 instanceof Container) {
            Containers.dropContents(bjw, fk, (Container)bwl7);
            bjw.updateNeighbourForOutputSignal(fk, this);
        }
        super.onRemove(byj1, bjw, fk, byj4, boolean5);
    }
    
    @Override
    public InteractionResult use(final BlockState byj, final Level bjw, final BlockPos fk, final Player ayj, final InteractionHand ajk, final BlockHitResult cvg) {
        if (bjw.isClientSide) {
            return InteractionResult.SUCCESS;
        }
        final MenuProvider ajo8 = this.getMenuProvider(byj, bjw, fk);
        if (ajo8 != null) {
            ayj.openMenu(ajo8);
            ayj.awardStat(this.getOpenChestStat());
        }
        return InteractionResult.SUCCESS;
    }
    
    protected Stat<ResourceLocation> getOpenChestStat() {
        return Stats.CUSTOM.get(Stats.OPEN_CHEST);
    }
    
    @Nullable
    public static Container getContainer(final ChestBlock bpw, final BlockState byj, final Level bjw, final BlockPos fk, final boolean boolean5) {
        return bpw.combine(byj, bjw, fk, boolean5).<Optional<Container>>apply(ChestBlock.CHEST_COMBINER).orElse(null);
    }
    
    @Override
    public DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> combine(final BlockState byj, final Level bjw, final BlockPos fk, final boolean boolean4) {
        BiPredicate<LevelAccessor, BlockPos> biPredicate6;
        if (boolean4) {
            biPredicate6 = ((bjx, fk) -> false);
        }
        else {
            biPredicate6 = ChestBlock::isChestBlockedAt;
        }
        return DoubleBlockCombiner.combineWithNeigbour(this.blockEntityType.get(), ChestBlock::getBlockType, ChestBlock::getConnectedDirection, ChestBlock.FACING, byj, bjw, fk, biPredicate6);
    }
    
    @Nullable
    @Override
    public MenuProvider getMenuProvider(final BlockState byj, final Level bjw, final BlockPos fk) {
        return this.combine(byj, bjw, fk, false).<Optional<MenuProvider>>apply(ChestBlock.MENU_PROVIDER_COMBINER).orElse(null);
    }
    
    public static DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction> opennessCombiner(final LidBlockEntity bxe) {
        return new DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction>() {
            @Override
            public Float2FloatFunction acceptDouble(final ChestBlockEntity bwp1, final ChestBlockEntity bwp2) {
                return float3 -> Math.max(bwp1.getOpenNess(float3), bwp2.getOpenNess(float3));
            }
            
            @Override
            public Float2FloatFunction acceptSingle(final ChestBlockEntity bwp) {
                return bwp::getOpenNess;
            }
            
            @Override
            public Float2FloatFunction acceptNone() {
                return bxe::getOpenNess;
            }
        };
    }
    
    @Override
    public BlockEntity newBlockEntity(final BlockGetter bjg) {
        return new ChestBlockEntity();
    }
    
    public static boolean isChestBlockedAt(final LevelAccessor bjx, final BlockPos fk) {
        return isBlockedChestByBlock(bjx, fk) || isCatSittingOnChest(bjx, fk);
    }
    
    private static boolean isBlockedChestByBlock(final BlockGetter bjg, final BlockPos fk) {
        final BlockPos fk2 = fk.above();
        return bjg.getBlockState(fk2).isRedstoneConductor(bjg, fk2);
    }
    
    private static boolean isCatSittingOnChest(final LevelAccessor bjx, final BlockPos fk) {
        final List<Cat> list3 = bjx.<Cat>getEntitiesOfClass(Cat.class, new AABB(fk.getX(), fk.getY() + 1, fk.getZ(), fk.getX() + 1, fk.getY() + 2, fk.getZ() + 1));
        if (!list3.isEmpty()) {
            for (final Cat ate5 : list3) {
                if (ate5.isSitting()) {
                    return true;
                }
            }
        }
        return false;
    }
    
    @Override
    public boolean hasAnalogOutputSignal(final BlockState byj) {
        return true;
    }
    
    @Override
    public int getAnalogOutputSignal(final BlockState byj, final Level bjw, final BlockPos fk) {
        return AbstractContainerMenu.getRedstoneSignalFromContainer(getContainer(this, byj, bjw, fk, false));
    }
    
    @Override
    public BlockState rotate(final BlockState byj, final Rotation btu) {
        return ((AbstractStateHolder<O, BlockState>)byj).<Comparable, Direction>setValue((Property<Comparable>)ChestBlock.FACING, btu.rotate(byj.<Direction>getValue((Property<Direction>)ChestBlock.FACING)));
    }
    
    @Override
    public BlockState mirror(final BlockState byj, final Mirror bsu) {
        return byj.rotate(bsu.getRotation(byj.<Direction>getValue((Property<Direction>)ChestBlock.FACING)));
    }
    
    @Override
    protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> a) {
        a.add(ChestBlock.FACING, ChestBlock.TYPE, ChestBlock.WATERLOGGED);
    }
    
    @Override
    public boolean isPathfindable(final BlockState byj, final BlockGetter bjg, final BlockPos fk, final PathComputationType cqr) {
        return false;
    }
    
    static {
        FACING = HorizontalDirectionalBlock.FACING;
        TYPE = BlockStateProperties.CHEST_TYPE;
        WATERLOGGED = BlockStateProperties.WATERLOGGED;
        NORTH_AABB = Block.box(1.0, 0.0, 0.0, 15.0, 14.0, 15.0);
        SOUTH_AABB = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 16.0);
        WEST_AABB = Block.box(0.0, 0.0, 1.0, 15.0, 14.0, 15.0);
        EAST_AABB = Block.box(1.0, 0.0, 1.0, 16.0, 14.0, 15.0);
        AABB = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 15.0);
        CHEST_COMBINER = new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<Container>>() {
            @Override
            public Optional<Container> acceptDouble(final ChestBlockEntity bwp1, final ChestBlockEntity bwp2) {
                return Optional.of(new CompoundContainer(bwp1, bwp2));
            }
            
            @Override
            public Optional<Container> acceptSingle(final ChestBlockEntity bwp) {
                return Optional.of(bwp);
            }
            
            @Override
            public Optional<Container> acceptNone() {
                return Optional.<Container>empty();
            }
        };
        MENU_PROVIDER_COMBINER = new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
            @Override
            public Optional<MenuProvider> acceptDouble(final ChestBlockEntity bwp1, final ChestBlockEntity bwp2) {
                final Container aje4 = new CompoundContainer(bwp1, bwp2);
                return Optional.of(new MenuProvider() {
                    @Nullable
                    @Override
                    public AbstractContainerMenu createMenu(final int integer, final Inventory ayi, final Player ayj) {
                        if (bwp1.canOpen(ayj) && bwp2.canOpen(ayj)) {
                            bwp1.unpackLootTable(ayi.player);
                            bwp2.unpackLootTable(ayi.player);
                            return ChestMenu.sixRows(integer, ayi, aje4);
                        }
                        return null;
                    }
                    
                    @Override
                    public Component getDisplayName() {
                        if (bwp1.hasCustomName()) {
                            return bwp1.getDisplayName();
                        }
                        if (bwp2.hasCustomName()) {
                            return bwp2.getDisplayName();
                        }
                        return new TranslatableComponent("container.chestDouble", new Object[0]);
                    }
                });
            }
            
            @Override
            public Optional<MenuProvider> acceptSingle(final ChestBlockEntity bwp) {
                return Optional.of(bwp);
            }
            
            @Override
            public Optional<MenuProvider> acceptNone() {
                return Optional.<MenuProvider>empty();
            }
        };
    }
}
