package net.minecraft.world.level.block.state.pattern;

import net.minecraft.world.phys.Vec3;
import com.google.common.base.MoreObjects;
import net.minecraft.core.Vec3i;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheBuilder;
import java.util.Iterator;
import net.minecraft.world.level.LevelReader;
import javax.annotation.Nullable;
import com.google.common.cache.LoadingCache;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import java.util.function.Predicate;

public class BlockPattern {
    private final Predicate<BlockInWorld>[][][] pattern;
    private final int depth;
    private final int height;
    private final int width;
    
    public BlockPattern(final Predicate<BlockInWorld>[][][] arr) {
        this.pattern = arr;
        this.depth = arr.length;
        if (this.depth > 0) {
            this.height = arr[0].length;
            if (this.height > 0) {
                this.width = arr[0][0].length;
            }
            else {
                this.width = 0;
            }
        }
        else {
            this.height = 0;
            this.width = 0;
        }
    }
    
    public int getDepth() {
        return this.depth;
    }
    
    public int getHeight() {
        return this.height;
    }
    
    public int getWidth() {
        return this.width;
    }
    
    @Nullable
    private BlockPatternMatch matches(final BlockPos fu, final Direction fz2, final Direction fz3, final LoadingCache<BlockPos, BlockInWorld> loadingCache) {
        for (int integer6 = 0; integer6 < this.width; ++integer6) {
            for (int integer7 = 0; integer7 < this.height; ++integer7) {
                for (int integer8 = 0; integer8 < this.depth; ++integer8) {
                    if (!this.pattern[integer8][integer7][integer6].test(loadingCache.getUnchecked(translateAndRotate(fu, fz2, fz3, integer6, integer7, integer8)))) {
                        return null;
                    }
                }
            }
        }
        return new BlockPatternMatch(fu, fz2, fz3, loadingCache, this.width, this.height, this.depth);
    }
    
    @Nullable
    public BlockPatternMatch find(final LevelReader bqd, final BlockPos fu) {
        final LoadingCache<BlockPos, BlockInWorld> loadingCache4 = createLevelCache(bqd, false);
        final int integer5 = Math.max(Math.max(this.width, this.height), this.depth);
        for (final BlockPos fu2 : BlockPos.betweenClosed(fu, fu.offset(integer5 - 1, integer5 - 1, integer5 - 1))) {
            for (final Direction fz11 : Direction.values()) {
                for (final Direction fz12 : Direction.values()) {
                    if (fz12 != fz11) {
                        if (fz12 != fz11.getOpposite()) {
                            final BlockPatternMatch b16 = this.matches(fu2, fz11, fz12, loadingCache4);
                            if (b16 != null) {
                                return b16;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
    
    public static LoadingCache<BlockPos, BlockInWorld> createLevelCache(final LevelReader bqd, final boolean boolean2) {
        return CacheBuilder.newBuilder().<BlockPos, BlockInWorld>build(new BlockCacheLoader(bqd, boolean2));
    }
    
    protected static BlockPos translateAndRotate(final BlockPos fu, final Direction fz2, final Direction fz3, final int integer4, final int integer5, final int integer6) {
        if (fz2 == fz3 || fz2 == fz3.getOpposite()) {
            throw new IllegalArgumentException("Invalid forwards & up combination");
        }
        final Vec3i gr7 = new Vec3i(fz2.getStepX(), fz2.getStepY(), fz2.getStepZ());
        final Vec3i gr8 = new Vec3i(fz3.getStepX(), fz3.getStepY(), fz3.getStepZ());
        final Vec3i gr9 = gr7.cross(gr8);
        return fu.offset(gr8.getX() * -integer5 + gr9.getX() * integer4 + gr7.getX() * integer6, gr8.getY() * -integer5 + gr9.getY() * integer4 + gr7.getY() * integer6, gr8.getZ() * -integer5 + gr9.getZ() * integer4 + gr7.getZ() * integer6);
    }
    
    static class BlockCacheLoader extends CacheLoader<BlockPos, BlockInWorld> {
        private final LevelReader level;
        private final boolean loadChunks;
        
        public BlockCacheLoader(final LevelReader bqd, final boolean boolean2) {
            this.level = bqd;
            this.loadChunks = boolean2;
        }
        
        @Override
        public BlockInWorld load(final BlockPos fu) throws Exception {
            return new BlockInWorld(this.level, fu, this.loadChunks);
        }
    }
    
    public static class BlockPatternMatch {
        private final BlockPos frontTopLeft;
        private final Direction forwards;
        private final Direction up;
        private final LoadingCache<BlockPos, BlockInWorld> cache;
        private final int width;
        private final int height;
        private final int depth;
        
        public BlockPatternMatch(final BlockPos fu, final Direction fz2, final Direction fz3, final LoadingCache<BlockPos, BlockInWorld> loadingCache, final int integer5, final int integer6, final int integer7) {
            this.frontTopLeft = fu;
            this.forwards = fz2;
            this.up = fz3;
            this.cache = loadingCache;
            this.width = integer5;
            this.height = integer6;
            this.depth = integer7;
        }
        
        public BlockPos getFrontTopLeft() {
            return this.frontTopLeft;
        }
        
        public Direction getForwards() {
            return this.forwards;
        }
        
        public Direction getUp() {
            return this.up;
        }
        
        public int getWidth() {
            return this.width;
        }
        
        public int getHeight() {
            return this.height;
        }
        
        public BlockInWorld getBlock(final int integer1, final int integer2, final int integer3) {
            return this.cache.getUnchecked(BlockPattern.translateAndRotate(this.frontTopLeft, this.getForwards(), this.getUp(), integer1, integer2, integer3));
        }
        
        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("up", this.up).add("forwards", this.forwards).add("frontTopLeft", this.frontTopLeft).toString();
        }
        
        public PortalInfo getPortalOutput(final Direction fz, final BlockPos fu, final double double3, final Vec3 dem, final double double5) {
            final Direction fz2 = this.getForwards();
            final Direction fz3 = fz2.getClockWise();
            final double double6 = this.getFrontTopLeft().getY() + 1 - double3 * this.getHeight();
            double double7;
            double double8;
            if (fz3 == Direction.NORTH) {
                double7 = fu.getX() + 0.5;
                double8 = this.getFrontTopLeft().getZ() + 1 - (1.0 - double5) * this.getWidth();
            }
            else if (fz3 == Direction.SOUTH) {
                double7 = fu.getX() + 0.5;
                double8 = this.getFrontTopLeft().getZ() + (1.0 - double5) * this.getWidth();
            }
            else if (fz3 == Direction.WEST) {
                double7 = this.getFrontTopLeft().getX() + 1 - (1.0 - double5) * this.getWidth();
                double8 = fu.getZ() + 0.5;
            }
            else {
                double7 = this.getFrontTopLeft().getX() + (1.0 - double5) * this.getWidth();
                double8 = fu.getZ() + 0.5;
            }
            double double9;
            double double10;
            if (fz2.getOpposite() == fz) {
                double9 = dem.x;
                double10 = dem.z;
            }
            else if (fz2.getOpposite() == fz.getOpposite()) {
                double9 = -dem.x;
                double10 = -dem.z;
            }
            else if (fz2.getOpposite() == fz.getClockWise()) {
                double9 = -dem.z;
                double10 = dem.x;
            }
            else {
                double9 = dem.z;
                double10 = -dem.x;
            }
            final int integer21 = (fz2.get2DDataValue() - fz.getOpposite().get2DDataValue()) * 90;
            return new PortalInfo(new Vec3(double7, double6, double8), new Vec3(double9, dem.y, double10), integer21);
        }
    }
    
    public static class PortalInfo {
        public final Vec3 pos;
        public final Vec3 speed;
        public final int angle;
        
        public PortalInfo(final Vec3 dem1, final Vec3 dem2, final int integer) {
            this.pos = dem1;
            this.speed = dem2;
            this.angle = integer;
        }
    }
}
