package net.minecraft.world.level.block;

import net.minecraft.world.level.block.state.StateHolder;
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.world.entity.monster.piglin.PiglinAi;
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.level.block.state.BlockBehaviour;
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(((((StateHolder<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 cfj) {
        final ChestType cgb2 = cfj.<ChestType>getValue(ChestBlock.TYPE);
        if (cgb2 == ChestType.SINGLE) {
            return DoubleBlockCombiner.BlockType.SINGLE;
        }
        if (cgb2 == ChestType.RIGHT) {
            return DoubleBlockCombiner.BlockType.FIRST;
        }
        return DoubleBlockCombiner.BlockType.SECOND;
    }
    
    @Override
    public RenderShape getRenderShape(final BlockState cfj) {
        return RenderShape.ENTITYBLOCK_ANIMATED;
    }
    
    @Override
    public BlockState updateShape(final BlockState cfj1, final Direction fz, final BlockState cfj3, final LevelAccessor bqc, final BlockPos fu5, final BlockPos fu6) {
        if (cfj1.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
            bqc.getLiquidTicks().scheduleTick(fu5, Fluids.WATER, Fluids.WATER.getTickDelay(bqc));
        }
        if (cfj3.is(this) && fz.getAxis().isHorizontal()) {
            final ChestType cgb8 = cfj3.<ChestType>getValue(ChestBlock.TYPE);
            if (cfj1.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE && cgb8 != ChestType.SINGLE && cfj1.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) == cfj3.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) && getConnectedDirection(cfj3) == fz.getOpposite()) {
                return ((StateHolder<O, BlockState>)cfj1).<ChestType, ChestType>setValue(ChestBlock.TYPE, cgb8.getOpposite());
            }
        }
        else if (getConnectedDirection(cfj1) == fz) {
            return ((StateHolder<O, BlockState>)cfj1).<ChestType, ChestType>setValue(ChestBlock.TYPE, ChestType.SINGLE);
        }
        return super.updateShape(cfj1, fz, cfj3, bqc, fu5, fu6);
    }
    
    @Override
    public VoxelShape getShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
        if (cfj.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) {
            return ChestBlock.AABB;
        }
        switch (getConnectedDirection(cfj)) {
            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 cfj) {
        final Direction fz2 = cfj.<Direction>getValue((Property<Direction>)ChestBlock.FACING);
        return (cfj.<ChestType>getValue(ChestBlock.TYPE) == ChestType.LEFT) ? fz2.getClockWise() : fz2.getCounterClockWise();
    }
    
    @Override
    public BlockState getStateForPlacement(final BlockPlaceContext bin) {
        ChestType cgb3 = ChestType.SINGLE;
        Direction fz4 = bin.getHorizontalDirection().getOpposite();
        final FluidState cxa5 = bin.getLevel().getFluidState(bin.getClickedPos());
        final boolean boolean6 = bin.isSecondaryUseActive();
        final Direction fz5 = bin.getClickedFace();
        if (fz5.getAxis().isHorizontal() && boolean6) {
            final Direction fz6 = this.candidatePartnerFacing(bin, fz5.getOpposite());
            if (fz6 != null && fz6.getAxis() != fz5.getAxis()) {
                fz4 = fz6;
                cgb3 = ((fz4.getCounterClockWise() == fz5.getOpposite()) ? ChestType.RIGHT : ChestType.LEFT);
            }
        }
        if (cgb3 == ChestType.SINGLE && !boolean6) {
            if (fz4 == this.candidatePartnerFacing(bin, fz4.getClockWise())) {
                cgb3 = ChestType.LEFT;
            }
            else if (fz4 == this.candidatePartnerFacing(bin, fz4.getCounterClockWise())) {
                cgb3 = ChestType.RIGHT;
            }
        }
        return ((((StateHolder<O, BlockState>)this.defaultBlockState()).setValue((Property<Comparable>)ChestBlock.FACING, fz4)).setValue(ChestBlock.TYPE, cgb3)).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, cxa5.getType() == Fluids.WATER);
    }
    
    @Override
    public FluidState getFluidState(final BlockState cfj) {
        if (cfj.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
            return Fluids.WATER.getSource(false);
        }
        return super.getFluidState(cfj);
    }
    
    @Nullable
    private Direction candidatePartnerFacing(final BlockPlaceContext bin, final Direction fz) {
        final BlockState cfj4 = bin.getLevel().getBlockState(bin.getClickedPos().relative(fz));
        return (cfj4.is(this) && cfj4.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) ? cfj4.<Direction>getValue((Property<Direction>)ChestBlock.FACING) : null;
    }
    
    @Override
    public void setPlacedBy(final Level bqb, final BlockPos fu, final BlockState cfj, final LivingEntity aoy, final ItemStack bki) {
        if (bki.hasCustomHoverName()) {
            final BlockEntity cdl7 = bqb.getBlockEntity(fu);
            if (cdl7 instanceof ChestBlockEntity) {
                ((ChestBlockEntity)cdl7).setCustomName(bki.getHoverName());
            }
        }
    }
    
    @Override
    public void onRemove(final BlockState cfj1, final Level bqb, final BlockPos fu, final BlockState cfj4, final boolean boolean5) {
        if (cfj1.is(cfj4.getBlock())) {
            return;
        }
        final BlockEntity cdl7 = bqb.getBlockEntity(fu);
        if (cdl7 instanceof Container) {
            Containers.dropContents(bqb, fu, (Container)cdl7);
            bqb.updateNeighbourForOutputSignal(fu, this);
        }
        super.onRemove(cfj1, bqb, fu, cfj4, boolean5);
    }
    
    @Override
    public InteractionResult use(final BlockState cfj, final Level bqb, final BlockPos fu, final Player bec, final InteractionHand anf, final BlockHitResult deh) {
        if (bqb.isClientSide) {
            return InteractionResult.SUCCESS;
        }
        final MenuProvider anj8 = this.getMenuProvider(cfj, bqb, fu);
        if (anj8 != null) {
            bec.openMenu(anj8);
            bec.awardStat(this.getOpenChestStat());
            PiglinAi.angerNearbyPiglins(bec, true);
        }
        return InteractionResult.CONSUME;
    }
    
    protected Stat<ResourceLocation> getOpenChestStat() {
        return Stats.CUSTOM.get(Stats.OPEN_CHEST);
    }
    
    @Nullable
    public static Container getContainer(final ChestBlock bwh, final BlockState cfj, final Level bqb, final BlockPos fu, final boolean boolean5) {
        return bwh.combine(cfj, bqb, fu, boolean5).<Optional<Container>>apply(ChestBlock.CHEST_COMBINER).orElse(null);
    }
    
    @Override
    public DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> combine(final BlockState cfj, final Level bqb, final BlockPos fu, final boolean boolean4) {
        BiPredicate<LevelAccessor, BlockPos> biPredicate6;
        if (boolean4) {
            biPredicate6 = ((bqc, fu) -> false);
        }
        else {
            biPredicate6 = ChestBlock::isChestBlockedAt;
        }
        return DoubleBlockCombiner.combineWithNeigbour(this.blockEntityType.get(), ChestBlock::getBlockType, ChestBlock::getConnectedDirection, ChestBlock.FACING, cfj, bqb, fu, biPredicate6);
    }
    
    @Nullable
    @Override
    public MenuProvider getMenuProvider(final BlockState cfj, final Level bqb, final BlockPos fu) {
        return this.combine(cfj, bqb, fu, false).<Optional<MenuProvider>>apply(ChestBlock.MENU_PROVIDER_COMBINER).orElse(null);
    }
    
    public static DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction> opennessCombiner(final LidBlockEntity cee) {
        return new DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction>() {
            @Override
            public Float2FloatFunction acceptDouble(final ChestBlockEntity cdp1, final ChestBlockEntity cdp2) {
                return float3 -> Math.max(cdp1.getOpenNess(float3), cdp2.getOpenNess(float3));
            }
            
            @Override
            public Float2FloatFunction acceptSingle(final ChestBlockEntity cdp) {
                return cdp::getOpenNess;
            }
            
            @Override
            public Float2FloatFunction acceptNone() {
                return cee::getOpenNess;
            }
        };
    }
    
    @Override
    public BlockEntity newBlockEntity(final BlockGetter bpg) {
        return new ChestBlockEntity();
    }
    
    public static boolean isChestBlockedAt(final LevelAccessor bqc, final BlockPos fu) {
        return isBlockedChestByBlock(bqc, fu) || isCatSittingOnChest(bqc, fu);
    }
    
    private static boolean isBlockedChestByBlock(final BlockGetter bpg, final BlockPos fu) {
        final BlockPos fu2 = fu.above();
        return bpg.getBlockState(fu2).isRedstoneConductor(bpg, fu2);
    }
    
    private static boolean isCatSittingOnChest(final LevelAccessor bqc, final BlockPos fu) {
        final List<Cat> list3 = bqc.<Cat>getEntitiesOfClass(Cat.class, new AABB(fu.getX(), fu.getY() + 1, fu.getZ(), fu.getX() + 1, fu.getY() + 2, fu.getZ() + 1));
        if (!list3.isEmpty()) {
            for (final Cat aym5 : list3) {
                if (aym5.isInSittingPose()) {
                    return true;
                }
            }
        }
        return false;
    }
    
    @Override
    public boolean hasAnalogOutputSignal(final BlockState cfj) {
        return true;
    }
    
    @Override
    public int getAnalogOutputSignal(final BlockState cfj, final Level bqb, final BlockPos fu) {
        return AbstractContainerMenu.getRedstoneSignalFromContainer(getContainer(this, cfj, bqb, fu, false));
    }
    
    @Override
    public BlockState rotate(final BlockState cfj, final Rotation cap) {
        return ((StateHolder<O, BlockState>)cfj).<Comparable, Direction>setValue((Property<Comparable>)ChestBlock.FACING, cap.rotate(cfj.<Direction>getValue((Property<Direction>)ChestBlock.FACING)));
    }
    
    @Override
    public BlockState mirror(final BlockState cfj, final Mirror bzj) {
        return cfj.rotate(bzj.getRotation(cfj.<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 cfj, final BlockGetter bpg, final BlockPos fu, final PathComputationType czg) {
        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 cdp1, final ChestBlockEntity cdp2) {
                return Optional.of(new CompoundContainer(cdp1, cdp2));
            }
            
            @Override
            public Optional<Container> acceptSingle(final ChestBlockEntity cdp) {
                return Optional.of(cdp);
            }
            
            @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 cdp1, final ChestBlockEntity cdp2) {
                final Container amz4 = new CompoundContainer(cdp1, cdp2);
                return Optional.of(new MenuProvider() {
                    @Nullable
                    @Override
                    public AbstractContainerMenu createMenu(final int integer, final Inventory beb, final Player bec) {
                        if (cdp1.canOpen(bec) && cdp2.canOpen(bec)) {
                            cdp1.unpackLootTable(beb.player);
                            cdp2.unpackLootTable(beb.player);
                            return ChestMenu.sixRows(integer, beb, amz4);
                        }
                        return null;
                    }
                    
                    @Override
                    public Component getDisplayName() {
                        if (cdp1.hasCustomName()) {
                            return cdp1.getDisplayName();
                        }
                        if (cdp2.hasCustomName()) {
                            return cdp2.getDisplayName();
                        }
                        return new TranslatableComponent("container.chestDouble");
                    }
                });
            }
            
            @Override
            public Optional<MenuProvider> acceptSingle(final ChestBlockEntity cdp) {
                return Optional.of(cdp);
            }
            
            @Override
            public Optional<MenuProvider> acceptNone() {
                return Optional.<MenuProvider>empty();
            }
        };
    }
}
