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

package com.hypixel.hytale.math.iterator;

import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;

public final class BlockIterator
{
    private BlockIterator() {
        throw new UnsupportedOperationException("This is a utilitiy class. Do not instantiate.");
    }
    
    public static boolean iterateFromTo(@Nonnull final Vector3d origin, @Nonnull final Vector3d target, @Nonnull final BlockIteratorProcedure procedure) {
        return iterateFromTo(origin.x, origin.y, origin.z, target.x, target.y, target.z, procedure);
    }
    
    public static boolean iterateFromTo(@Nonnull final Vector3i origin, @Nonnull final Vector3i target, @Nonnull final BlockIteratorProcedure procedure) {
        return iterateFromTo(origin.x, origin.y, origin.z, target.x, target.y, target.z, procedure);
    }
    
    public static boolean iterateFromTo(final double sx, final double sy, final double sz, final double tx, final double ty, final double tz, @Nonnull final BlockIteratorProcedure procedure) {
        final double dx = tx - sx;
        final double dy = ty - sy;
        final double dz = tz - sz;
        final double maxDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
        return iterate(sx, sy, sz, dx, dy, dz, maxDistance, procedure);
    }
    
    public static <T> boolean iterateFromTo(final double sx, final double sy, final double sz, final double tx, final double ty, final double tz, @Nonnull final BlockIteratorProcedurePlus1<T> procedure, final T t) {
        final double dx = tx - sx;
        final double dy = ty - sy;
        final double dz = tz - sz;
        final double maxDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
        return iterate(sx, sy, sz, dx, dy, dz, maxDistance, procedure, t);
    }
    
    public static boolean iterate(@Nonnull final Vector3d origin, @Nonnull final Vector3d direction, final double maxDistance, @Nonnull final BlockIteratorProcedure procedure) {
        return iterate(origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, maxDistance, procedure);
    }
    
    public static boolean iterate(final double sx, final double sy, final double sz, final double dx, final double dy, final double dz, final double maxDistance, @Nonnull final BlockIteratorProcedure procedure) {
        checkParameters(sx, sy, sz, dx, dy, dz);
        return iterate0(sx, sy, sz, dx, dy, dz, maxDistance, procedure);
    }
    
    private static boolean iterate0(final double sx, final double sy, final double sz, final double dx, final double dy, final double dz, double maxDistance, @Nonnull final BlockIteratorProcedure procedure) {
        maxDistance /= Math.sqrt(dx * dx + dy * dy + dz * dz);
        int bx = (int)FastMath.fastFloor(sx);
        int by = (int)FastMath.fastFloor(sy);
        int bz = (int)FastMath.fastFloor(sz);
        double px = sx - bx;
        double py = sy - by;
        double pz = sz - bz;
        double t;
        double qx;
        double qy;
        double qz;
        for (double pt = 0.0; pt <= maxDistance; pt += t, px = qx, py = qy, pz = qz) {
            t = intersection(px, py, pz, dx, dy, dz);
            qx = px + t * dx;
            qy = py + t * dy;
            qz = pz + t * dz;
            if (!procedure.accept(bx, by, bz, px, py, pz, qx, qy, qz)) {
                return false;
            }
            if (dx < 0.0 && FastMath.sEq(qx, 0.0)) {
                ++qx;
                --bx;
            }
            else if (dx > 0.0 && FastMath.gEq(qx, 1.0)) {
                --qx;
                ++bx;
            }
            if (dy < 0.0 && FastMath.sEq(qy, 0.0)) {
                ++qy;
                --by;
            }
            else if (dy > 0.0 && FastMath.gEq(qy, 1.0)) {
                --qy;
                ++by;
            }
            if (dz < 0.0 && FastMath.sEq(qz, 0.0)) {
                ++qz;
                --bz;
            }
            else if (dz > 0.0 && FastMath.gEq(qz, 1.0)) {
                --qz;
                ++bz;
            }
        }
        return true;
    }
    
    public static <T> boolean iterate(final double sx, final double sy, final double sz, final double dx, final double dy, final double dz, final double maxDistance, @Nonnull final BlockIteratorProcedurePlus1<T> procedure, final T obj1) {
        checkParameters(sx, sy, sz, dx, dy, dz);
        return iterate0(sx, sy, sz, dx, dy, dz, maxDistance, (BlockIteratorProcedurePlus1<Object>)procedure, obj1);
    }
    
    private static <T> boolean iterate0(final double sx, final double sy, final double sz, final double dx, final double dy, final double dz, double maxDistance, @Nonnull final BlockIteratorProcedurePlus1<T> procedure, final T obj1) {
        maxDistance /= Math.sqrt(dx * dx + dy * dy + dz * dz);
        int bx = (int)FastMath.fastFloor(sx);
        int by = (int)FastMath.fastFloor(sy);
        int bz = (int)FastMath.fastFloor(sz);
        double px = sx - bx;
        double py = sy - by;
        double pz = sz - bz;
        double t;
        double qx;
        double qy;
        double qz;
        for (double pt = 0.0; pt <= maxDistance; pt += t, px = qx, py = qy, pz = qz) {
            t = intersection(px, py, pz, dx, dy, dz);
            qx = px + t * dx;
            qy = py + t * dy;
            qz = pz + t * dz;
            if (!procedure.accept(bx, by, bz, px, py, pz, qx, qy, qz, obj1)) {
                return false;
            }
            if (dx < 0.0 && FastMath.sEq(qx, 0.0)) {
                ++qx;
                --bx;
            }
            else if (dx > 0.0 && FastMath.gEq(qx, 1.0)) {
                --qx;
                ++bx;
            }
            if (dy < 0.0 && FastMath.sEq(qy, 0.0)) {
                ++qy;
                --by;
            }
            else if (dy > 0.0 && FastMath.gEq(qy, 1.0)) {
                --qy;
                ++by;
            }
            if (dz < 0.0 && FastMath.sEq(qz, 0.0)) {
                ++qz;
                --bz;
            }
            else if (dz > 0.0 && FastMath.gEq(qz, 1.0)) {
                --qz;
                ++bz;
            }
        }
        return true;
    }
    
    private static void checkParameters(final double sx, final double sy, final double sz, final double dx, final double dy, final double dz) {
        if (isNonValidNumber(sx)) {
            throw new IllegalArgumentException("sx is a non-valid number! Given: " + sx);
        }
        if (isNonValidNumber(sy)) {
            throw new IllegalArgumentException("sy is a non-valid number! Given: " + sy);
        }
        if (isNonValidNumber(sz)) {
            throw new IllegalArgumentException("sz is a non-valid number! Given: " + sz);
        }
        if (isNonValidNumber(dx)) {
            throw new IllegalArgumentException("dx is a non-valid number! Given: " + dx);
        }
        if (isNonValidNumber(dy)) {
            throw new IllegalArgumentException("dy is a non-valid number! Given: " + dy);
        }
        if (isNonValidNumber(dz)) {
            throw new IllegalArgumentException("dz is a non-valid number! Given: " + dz);
        }
        if (isZeroDirection(dx, dy, dz)) {
            throw new IllegalArgumentException("Direction is ZERO! Given: (" + dx + ", " + dy + ", " + dz);
        }
    }
    
    public static boolean isNonValidNumber(final double d) {
        return Double.isNaN(d) || Double.isInfinite(d);
    }
    
    public static boolean isZeroDirection(final double dx, final double dy, final double dz) {
        return FastMath.eq(dx, 0.0) && FastMath.eq(dy, 0.0) && FastMath.eq(dz, 0.0);
    }
    
    private static double intersection(final double px, final double py, final double pz, final double dx, final double dy, final double dz) {
        double tFar = 0.0;
        if (dx < 0.0) {
            final double t = -px / dx;
            final double u = pz + dz * t;
            final double v = py + dy * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        else if (dx > 0.0) {
            final double t = (1.0 - px) / dx;
            final double u = pz + dz * t;
            final double v = py + dy * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        if (dy < 0.0) {
            final double t = -py / dy;
            final double u = px + dx * t;
            final double v = pz + dz * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        else if (dy > 0.0) {
            final double t = (1.0 - py) / dy;
            final double u = px + dx * t;
            final double v = pz + dz * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        if (dz < 0.0) {
            final double t = -pz / dz;
            final double u = px + dx * t;
            final double v = py + dy * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        else if (dz > 0.0) {
            final double t = (1.0 - pz) / dz;
            final double u = px + dx * t;
            final double v = py + dy * t;
            if (t > tFar && FastMath.gEq(u, 0.0) && FastMath.sEq(u, 1.0) && FastMath.gEq(v, 0.0) && FastMath.sEq(v, 1.0)) {
                tFar = t;
            }
        }
        return tFar;
    }
    
    static class FastMath
    {
        static final double TWO_POWER_52 = 4.503599627370496E15;
        static final double ROUNDING_ERROR = 1.0E-15;
        
        static boolean eq(final double a, final double b) {
            return abs(a - b) < 1.0E-15;
        }
        
        static boolean sEq(final double a, final double b) {
            return a <= b + 1.0E-15;
        }
        
        static boolean gEq(final double a, final double b) {
            return a >= b - 1.0E-15;
        }
        
        static double abs(final double x) {
            return (x < 0.0) ? (-x) : x;
        }
        
        static long fastFloor(final double x) {
            if (x >= 4.503599627370496E15 || x <= -4.503599627370496E15) {
                return (long)x;
            }
            long y = (long)x;
            if (x < 0.0 && y != x) {
                --y;
            }
            if (y == 0L) {
                return (long)(x * y);
            }
            return y;
        }
    }
    
    @FunctionalInterface
    public interface BlockIteratorProcedure
    {
        boolean accept(final int p0, final int p1, final int p2, final double p3, final double p4, final double p5, final double p6, final double p7, final double p8);
    }
    
    @FunctionalInterface
    public interface BlockIteratorProcedurePlus1<T>
    {
        boolean accept(final int p0, final int p1, final int p2, final double p3, final double p4, final double p5, final double p6, final double p7, final double p8, final T p9);
    }
}
