// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.math.util;

import java.util.function.DoubleUnaryOperator;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.function.BiPredicate;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;

public final class NearestBlockUtil
{
    public static final IterationElement[] DEFAULT_ELEMENTS;
    
    private NearestBlockUtil() {
        throw new UnsupportedOperationException();
    }
    
    @Nullable
    public static <T> Vector3i findNearestBlock(@Nonnull final Vector3d position, @Nonnull final BiPredicate<Vector3i, T> validBlock, final T t) {
        return findNearestBlock(NearestBlockUtil.DEFAULT_ELEMENTS, position.getX(), position.getY(), position.getZ(), validBlock, t);
    }
    
    @Nullable
    public static <T> Vector3i findNearestBlock(@Nonnull final IterationElement[] elements, @Nonnull final Vector3d position, @Nonnull final BiPredicate<Vector3i, T> validBlock, final T t) {
        return findNearestBlock(elements, position.getX(), position.getY(), position.getZ(), validBlock, t);
    }
    
    @Nullable
    public static <T> Vector3i findNearestBlock(final double x, final double y, final double z, @Nonnull final BiPredicate<Vector3i, T> validBlock, final T t) {
        return findNearestBlock(NearestBlockUtil.DEFAULT_ELEMENTS, x, y, z, validBlock, t);
    }
    
    @Nullable
    public static <T> Vector3i findNearestBlock(@Nonnull final IterationElement[] elements, final double x, final double y, final double z, @Nonnull final BiPredicate<Vector3i, T> validBlock, final T t) {
        final int blockX = MathUtil.floor(x);
        final int blockY = MathUtil.floor(y);
        final int blockZ = MathUtil.floor(z);
        final double rx = x % 1.0;
        final double ry = y % 1.0;
        final double rz = z % 1.0;
        Vector3i nearest = null;
        final Vector3i tmp = new Vector3i();
        double nearestDist = Double.POSITIVE_INFINITY;
        for (final IterationElement element : elements) {
            final double dx = rx - element.getX().applyAsDouble(rx);
            final double dy = ry - element.getY().applyAsDouble(ry);
            final double dz = rz - element.getZ().applyAsDouble(rz);
            final double dist = dx * dx + dy * dy + dz * dz;
            tmp.assign(blockX + element.getOffsetX(), blockY + element.getOffsetY(), blockZ + element.getOffsetZ());
            if (dist < nearestDist && validBlock.test(tmp, t)) {
                nearestDist = dist;
                if (nearest == null) {
                    nearest = new Vector3i();
                }
                nearest.assign(tmp);
            }
        }
        return nearest;
    }
    
    static {
        DEFAULT_ELEMENTS = new IterationElement[] { new IterationElement(-1, 0, 0, x -> 0.0, y -> y, z -> z), new IterationElement(1, 0, 0, x -> 1.0, y -> y, z -> z), new IterationElement(0, -1, 0, x -> x, y -> 0.0, z -> z), new IterationElement(0, 1, 0, x -> x, y -> 1.0, z -> z), new IterationElement(0, 0, -1, x -> x, y -> y, z -> 0.0), new IterationElement(0, 0, 1, x -> x, y -> y, z -> 1.0) };
    }
    
    public static class IterationElement
    {
        private final int ox;
        private final int oy;
        private final int oz;
        private final DoubleUnaryOperator x;
        private final DoubleUnaryOperator y;
        private final DoubleUnaryOperator z;
        
        public IterationElement(final int ox, final int oy, final int oz, final DoubleUnaryOperator x, final DoubleUnaryOperator y, final DoubleUnaryOperator z) {
            this.ox = ox;
            this.oy = oy;
            this.oz = oz;
            this.x = x;
            this.y = y;
            this.z = z;
        }
        
        public int getOffsetX() {
            return this.ox;
        }
        
        public int getOffsetY() {
            return this.oy;
        }
        
        public int getOffsetZ() {
            return this.oz;
        }
        
        public DoubleUnaryOperator getX() {
            return this.x;
        }
        
        public DoubleUnaryOperator getY() {
            return this.y;
        }
        
        public DoubleUnaryOperator getZ() {
            return this.z;
        }
    }
}
