// 
// 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;

public class BlockCylinderUtil
{
    public static <T> boolean forEachBlock(final int originX, final int originY, final int originZ, final int radiusX, final int height, final int radiusZ, final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (radiusX <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusX));
        }
        if (height <= 0) {
            throw new IllegalArgumentException(String.valueOf(height));
        }
        if (radiusZ <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusZ));
        }
        final float radiusXAdjusted = radiusX + 0.41f;
        final float radiusZAdjusted = radiusZ + 0.41f;
        final double invRadiusXSqr = 1.0 / (radiusXAdjusted * radiusXAdjusted);
        for (int x = -radiusX; x <= radiusX; ++x) {
            final double qx = 1.0 - x * x * invRadiusXSqr;
            final double dz = Math.sqrt(qx) * radiusZAdjusted;
            int maxZ;
            int z;
            for (int minZ = z = -(maxZ = (int)dz); z <= maxZ; ++z) {
                for (int y = height - 1; y >= 0; --y) {
                    if (!consumer.test(originX + x, originY + y, originZ + z, t)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    public static <T> boolean forEachBlock(final int originX, final int originY, final int originZ, final int radiusX, final int height, final int radiusZ, final int thickness, final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, thickness, false, t, consumer);
    }
    
    public static <T> boolean forEachBlock(final int originX, final int originY, final int originZ, final int radiusX, final int height, final int radiusZ, final int thickness, final boolean capped, final T t, @Nonnull final TriIntObjPredicate<T> consumer) {
        if (thickness < 1) {
            return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, t, consumer);
        }
        if (radiusX <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusX));
        }
        if (height <= 0) {
            throw new IllegalArgumentException(String.valueOf(height));
        }
        if (radiusZ <= 0) {
            throw new IllegalArgumentException(String.valueOf(radiusZ));
        }
        final float radiusXAdjusted = radiusX + 0.41f;
        final float radiusZAdjusted = radiusZ + 0.41f;
        final float innerRadiusXAdjusted = radiusXAdjusted - thickness;
        final float innerRadiusZAdjusted = radiusZAdjusted - thickness;
        if (innerRadiusXAdjusted <= 0.0f || innerRadiusZAdjusted <= 0.0f) {
            return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, t, consumer);
        }
        final double invRadiusXSqr = 1.0 / (radiusXAdjusted * radiusXAdjusted);
        final double invInnerRadiusXSqr = 1.0 / (innerRadiusXAdjusted * innerRadiusXAdjusted);
        final int innerMinY = thickness;
        final int innerMaxY = height - thickness;
        for (int y = height - 1; y >= 0; --y) {
            final boolean cap = capped && (y < innerMinY || y > innerMaxY);
            for (int x = -radiusX; x <= radiusX; ++x) {
                final double qx = 1.0 - x * x * invRadiusXSqr;
                final double dz = Math.sqrt(qx) * radiusZAdjusted;
                final int maxZ = (int)dz;
                final double innerQx = (x < innerRadiusXAdjusted) ? (1.0 - x * x * invInnerRadiusXSqr) : 0.0;
                final double innerDZ = (innerQx > 0.0) ? (Math.sqrt(innerQx) * innerRadiusZAdjusted) : 0.0;
                int z;
                final int minZ = z = (cap ? 0 : MathUtil.ceil(innerDZ));
                if (z == 0) {
                    if (!consumer.test(originX + x, originY + y, originZ, t)) {
                        return false;
                    }
                    ++z;
                }
                while (z <= maxZ) {
                    if (!consumer.test(originX + x, originY + y, originZ + z, t)) {
                        return false;
                    }
                    if (!consumer.test(originX + x, originY + y, originZ - z, t)) {
                        return false;
                    }
                    ++z;
                }
            }
        }
        return true;
    }
}
