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

package com.hypixel.hytale.math.block;

import com.hypixel.hytale.math.util.MathUtil;
import javax.annotation.Nonnull;
import com.hypixel.hytale.function.predicate.TriIntObjPredicate;
import javax.annotation.Nullable;

public class BlockSphereUtil
{
    public static <T> void forEachBlockExact(final int originX, final int originY, final int originZ, final double radius, @Nullable final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (radius <= 0.0) {
            throw new IllegalArgumentException(String.valueOf(radius));
        }
        final int ceiledRadius = MathUtil.ceil(radius);
        final double invRadiusXSqr = 1.0 / (ceiledRadius * ceiledRadius);
        final double invRadiusYSqr = 1.0 / (ceiledRadius * ceiledRadius);
        for (int x = -ceiledRadius; x <= ceiledRadius; ++x) {
            final double qx = 1.0 - x * x * invRadiusXSqr;
            final double dy = Math.sqrt(qx) * ceiledRadius;
            int maxY;
            for (int minY = -(maxY = (int)dy), y = maxY; y >= minY; --y) {
                final double dz = Math.sqrt(qx - y * y * invRadiusYSqr) * ceiledRadius;
                int maxZ;
                int z;
                for (int minZ = z = -(maxZ = (int)dz); z <= maxZ; ++z) {
                    if (!consumer.test(originX + x, originY + y, originZ + z, t)) {
                        return;
                    }
                }
            }
        }
    }
    
    public static <T> void forEachBlock(final int originX, final int originY, final int originZ, final int radius, @Nullable final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        forEachBlock(originX, originY, originZ, radius, radius, radius, t, consumer);
    }
    
    public static <T> boolean forEachBlock(final int originX, final int originY, final int originZ, final int radiusX, final int radiusY, final int radiusZ, @Nullable final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (radiusX <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusX));
        }
        if (radiusY <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusY));
        }
        if (radiusZ <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusZ));
        }
        final float radiusXAdjusted = radiusX + 0.41f;
        final float radiusYAdjusted = radiusY + 0.41f;
        final float radiusZAdjusted = radiusZ + 0.41f;
        final float invRadiusXSqr = 1.0f / (radiusXAdjusted * radiusXAdjusted);
        final float invRadiusYSqr = 1.0f / (radiusYAdjusted * radiusYAdjusted);
        for (int x = 0; x <= radiusX; ++x) {
            final float qx = 1.0f - x * x * invRadiusXSqr;
            final double dy = Math.sqrt(qx) * radiusYAdjusted;
            for (int maxY = (int)dy, y = 0; y <= maxY; ++y) {
                final double dz = Math.sqrt(qx - y * y * invRadiusYSqr) * radiusZAdjusted;
                for (int maxZ = (int)dz, z = 0; z <= maxZ; ++z) {
                    if (!test(originX, originY, originZ, x, y, z, t, (TriIntObjPredicate<Object>)consumer)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    public static <T> void forEachBlock(final int originX, final int originY, final int originZ, final int radius, final int thickness, @Nullable final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        forEachBlock(originX, originY, originZ, radius, radius, radius, thickness, t, consumer);
    }
    
    public static <T> boolean forEachBlock(final int originX, final int originY, final int originZ, final int radiusX, final int radiusY, final int radiusZ, final int thickness, @Nullable final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (thickness < 1) {
            return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, t, consumer);
        }
        if (radiusX <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusX));
        }
        if (radiusY <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusY));
        }
        if (radiusZ <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusZ));
        }
        final float radiusXAdjusted = radiusX + 0.41f;
        final float radiusYAdjusted = radiusY + 0.41f;
        final float radiusZAdjusted = radiusZ + 0.41f;
        final float innerRadiusXAdjusted = radiusXAdjusted - thickness;
        final float innerRadiusYAdjusted = radiusYAdjusted - thickness;
        final float innerRadiusZAdjusted = radiusZAdjusted - thickness;
        final float invRadiusX2 = 1.0f / (radiusXAdjusted * radiusXAdjusted);
        final float invRadiusY2 = 1.0f / (radiusYAdjusted * radiusYAdjusted);
        final float invRadiusZ2 = 1.0f / (radiusZAdjusted * radiusZAdjusted);
        final float invInnerRadiusX2 = 1.0f / (innerRadiusXAdjusted * innerRadiusXAdjusted);
        final float invInnerRadiusY2 = 1.0f / (innerRadiusYAdjusted * innerRadiusYAdjusted);
        final float invInnerRadiusZ2 = 1.0f / (innerRadiusZAdjusted * innerRadiusZAdjusted);
        for (int y = 0, y2 = 1; y <= radiusY; ++y, ++y2) {
            final float qy = y * y * invRadiusY2;
            final double dx = Math.sqrt(1.0f - qy) * radiusXAdjusted;
            final int maxX = (int)dx;
            final float innerQy = y * y * invInnerRadiusY2;
            final float outerQy = y2 * y2 * invRadiusY2;
            for (int x = 0, x2 = 1; x <= maxX; ++x, ++x2) {
                final float qx = x * x * invRadiusX2;
                final double dz = Math.sqrt(1.0f - qx - qy) * radiusZAdjusted;
                final int maxZ = (int)dz;
                final float innerQx = x * x * invInnerRadiusX2;
                final float outerQx = x2 * x2 * invRadiusX2;
                for (int z = 0, z2 = 1; z <= maxZ; ++z, ++z2) {
                    final float innerQz = z * z * invInnerRadiusZ2;
                    if (innerQx + innerQy + innerQz < 1.0f) {
                        final float outerQz = z2 * z2 * invRadiusZ2;
                        if (outerQx + outerQy + outerQz < 1.0f) {
                            continue;
                        }
                    }
                    if (!test(originX, originY, originZ, x, y, z, t, consumer)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    private static <T> boolean test(final int originX, final int originY, final int originZ, final int x, final int y, final int z, final T context, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (!consumer.test(originX + x, originY + y, originZ + z, context)) {
            return false;
        }
        if (x > 0) {
            if (!consumer.test(originX - x, originY + y, originZ + z, context)) {
                return false;
            }
            if (y > 0 && !consumer.test(originX - x, originY - y, originZ + z, context)) {
                return false;
            }
            if (z > 0 && !consumer.test(originX - x, originY + y, originZ - z, context)) {
                return false;
            }
            if (y > 0 && z > 0 && !consumer.test(originX - x, originY - y, originZ - z, context)) {
                return false;
            }
        }
        if (y > 0) {
            if (!consumer.test(originX + x, originY - y, originZ + z, context)) {
                return false;
            }
            if (z > 0 && !consumer.test(originX + x, originY - y, originZ - z, context)) {
                return false;
            }
        }
        return z <= 0 || consumer.test(originX + x, originY + y, originZ - z, context);
    }
}
