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

package com.hypixel.hytale.math.util;

import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3d;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.concurrent.ThreadLocalRandom;

public class MathUtil
{
    public static final double EPSILON_DOUBLE;
    public static final float EPSILON_FLOAT;
    public static float PITCH_EDGE_PADDING;
    
    public static int abs(final int i) {
        final int mask = i >> 31;
        return i + mask ^ mask;
    }
    
    public static int floor(final double d) {
        final int i = (int)d;
        if (i <= d) {
            return i;
        }
        if (d < -2.147483648E9) {
            return Integer.MIN_VALUE;
        }
        return i - 1;
    }
    
    public static int ceil(final double d) {
        final int i = (int)d;
        if (d <= 0.0 || d == i) {
            return i;
        }
        if (d > 2.147483647E9) {
            return Integer.MAX_VALUE;
        }
        return i + 1;
    }
    
    public static int randomInt(final int min, final int max) {
        return ThreadLocalRandom.current().nextInt(min, max);
    }
    
    public static double randomDouble(final double min, final double max) {
        return min + Math.random() * (max - min);
    }
    
    public static float randomFloat(final float min, final float max) {
        return min + (float)Math.random() * (max - min);
    }
    
    public static double round(final double d, final int p) {
        final double pow = Math.pow(10.0, p);
        return Math.round(d * pow) / pow;
    }
    
    public static boolean within(final double val, final double min, final double max) {
        return val >= min && val <= max;
    }
    
    public static double minValue(double v, final double a, final double c) {
        if (a < v) {
            v = a;
        }
        if (c < v) {
            v = c;
        }
        return v;
    }
    
    public static int minValue(int v, final int a, final int c) {
        if (a < v) {
            v = a;
        }
        if (c < v) {
            v = c;
        }
        return v;
    }
    
    public static double maxValue(double v, final double a, final double b, final double c) {
        if (a > v) {
            v = a;
        }
        if (b > v) {
            v = b;
        }
        if (c > v) {
            v = c;
        }
        return v;
    }
    
    public static double maxValue(double v, final double a, final double b) {
        if (a > v) {
            v = a;
        }
        if (b > v) {
            v = b;
        }
        return v;
    }
    
    public static byte maxValue(byte v, final byte a, final byte b) {
        if (a > v) {
            v = a;
        }
        if (b > v) {
            v = b;
        }
        return v;
    }
    
    public static byte maxValue(byte v, final byte a, final byte b, final byte c) {
        if (a > v) {
            v = a;
        }
        if (b > v) {
            v = b;
        }
        if (c > v) {
            v = c;
        }
        return v;
    }
    
    public static int maxValue(int v, final int a, final int b) {
        if (a > v) {
            v = a;
        }
        if (b > v) {
            v = b;
        }
        return v;
    }
    
    public static double lengthSquared(final double x, final double y) {
        return x * x + y * y;
    }
    
    public static double length(final double x, final double y) {
        return Math.sqrt(lengthSquared(x, y));
    }
    
    public static double lengthSquared(final double x, final double y, final double z) {
        return x * x + y * y + z * z;
    }
    
    public static double length(final double x, final double y, final double z) {
        return Math.sqrt(lengthSquared(x, y, z));
    }
    
    public static double maxValue(final double v, final double a) {
        return (a > v) ? a : v;
    }
    
    public static double clipToZero(final double v) {
        return clipToZero(v, MathUtil.EPSILON_DOUBLE);
    }
    
    public static double clipToZero(final double v, final double epsilon) {
        return (v >= -epsilon && v <= epsilon) ? 0.0 : v;
    }
    
    public static float clipToZero(final float v) {
        return clipToZero(v, MathUtil.EPSILON_FLOAT);
    }
    
    public static float clipToZero(final float v, final float epsilon) {
        return (v >= -epsilon && v <= epsilon) ? 0.0f : v;
    }
    
    public static boolean closeToZero(final double v) {
        return closeToZero(v, MathUtil.EPSILON_DOUBLE);
    }
    
    public static boolean closeToZero(final double v, final double epsilon) {
        return v >= -epsilon && v <= epsilon;
    }
    
    public static boolean closeToZero(final float v) {
        return closeToZero(v, MathUtil.EPSILON_FLOAT);
    }
    
    public static boolean closeToZero(final float v, final float epsilon) {
        return v >= -epsilon && v <= epsilon;
    }
    
    public static double clamp(final double v, final double min, final double max) {
        if (v > max) {
            return (v < min) ? min : max;
        }
        return (v < min) ? min : v;
    }
    
    public static float clamp(final float v, final float min, final float max) {
        if (v > max) {
            return (v < min) ? min : max;
        }
        return (v < min) ? min : v;
    }
    
    public static int clamp(final int v, final int min, final int max) {
        if (v > max) {
            return (v < min) ? min : max;
        }
        return (v < min) ? min : v;
    }
    
    public static long clamp(final long v, final long min, final long max) {
        if (v > max) {
            return (v < min) ? min : max;
        }
        return (v < min) ? min : v;
    }
    
    public static int getPercentageOf(final int index, final int max) {
        return (int)(index / (max - 1.0) * 100.0);
    }
    
    public static double percent(final int v, final int total) {
        if (total == 0) {
            return 0.0;
        }
        return v * 100.0 / total;
    }
    
    public static int fastRound(final float f) {
        return fastFloor(f + 0.5f);
    }
    
    public static long fastRound(final double d) {
        return fastFloor(d + 0.5);
    }
    
    public static int fastFloor(final float f) {
        final int i = (int)f;
        if (i <= f) {
            return i;
        }
        if (f < -2.1474836E9f) {
            return Integer.MIN_VALUE;
        }
        return i - 1;
    }
    
    public static long fastFloor(final double d) {
        final long i = (long)d;
        if (i <= d) {
            return i;
        }
        if (d < -9.223372036854776E18) {
            return Long.MIN_VALUE;
        }
        return i - 1L;
    }
    
    public static int fastCeil(final float f) {
        final int i = (int)f;
        if (f <= 0.0f || f == i) {
            return i;
        }
        if (f > 2.1474836E9f) {
            return Integer.MAX_VALUE;
        }
        return i + 1;
    }
    
    public static long fastCeil(final double d) {
        final long i = (long)d;
        if (d <= 0.0 || d == i) {
            return i;
        }
        if (d > 9.223372036854776E18) {
            return Long.MAX_VALUE;
        }
        return i + 1L;
    }
    
    private MathUtil() {
    }
    
    public static float halfFloatToFloat(final int hbits) {
        int mant = hbits & 0x3FF;
        int exp = hbits & 0x7C00;
        if (exp == 31744) {
            exp = 261120;
        }
        else if (exp != 0) {
            exp += 114688;
            if (mant == 0 && exp > 115712) {
                return Float.intBitsToFloat((hbits & 0x8000) << 16 | exp << 13 | 0x3FF);
            }
        }
        else if (mant != 0) {
            exp = 115712;
            do {
                mant <<= 1;
                exp -= 1024;
            } while ((mant & 0x400) == 0x0);
            mant &= 0x3FF;
        }
        return Float.intBitsToFloat((hbits & 0x8000) << 16 | (exp | mant) << 13);
    }
    
    public static int halfFloatFromFloat(final float fval) {
        final int fbits = Float.floatToIntBits(fval);
        final int sign = fbits >>> 16 & 0x8000;
        int val = (fbits & Integer.MAX_VALUE) + 4096;
        if (val >= 1199570944) {
            if ((fbits & Integer.MAX_VALUE) < 1199570944) {
                return sign | 0x7BFF;
            }
            if (val < 2139095040) {
                return sign | 0x7C00;
            }
            return sign | 0x7C00 | (fbits & 0x7FFFFF) >>> 13;
        }
        else {
            if (val >= 947912704) {
                return sign | val - 939524096 >>> 13;
            }
            if (val < 855638016) {
                return sign;
            }
            val = (fbits & Integer.MAX_VALUE) >>> 23;
            return sign | ((fbits & 0x7FFFFF) | 0x800000) + (8388608 >>> val - 102) >>> 126 - val;
        }
    }
    
    public static int byteCount(final int i) {
        if (i > 65535) {
            return 4;
        }
        if (i > 255) {
            return 2;
        }
        if (i > 0) {
            return 1;
        }
        return 0;
    }
    
    public static int packInt(final int x, final int z) {
        return x << 16 | (z & 0xFFFF);
    }
    
    public static int unpackLeft(final int packed) {
        int i = packed >> 16 & 0xFFFF;
        if ((i & 0x8000) != 0x0) {
            i |= 0xFFFF0000;
        }
        return i;
    }
    
    public static int unpackRight(final int packed) {
        int i = packed & 0xFFFF;
        if ((i & 0x8000) != 0x0) {
            i |= 0xFFFF0000;
        }
        return i;
    }
    
    public static long packLong(final int left, final int right) {
        return (long)left << 32 | ((long)right & 0xFFFFFFFFL);
    }
    
    public static int unpackLeft(final long packed) {
        return (int)(packed >> 32);
    }
    
    public static int unpackRight(final long packed) {
        return (int)packed;
    }
    
    @Nonnull
    public static Vector3i rotateVectorYAxis(@Nonnull final Vector3i vector, final int angle, final boolean clockwise) {
        final float radAngle = 0.017453292f * angle;
        int x1;
        int z1;
        if (clockwise) {
            x1 = (int)(vector.x * TrigMathUtil.cos(radAngle) - vector.z * TrigMathUtil.sin(radAngle));
            z1 = (int)(vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle));
        }
        else {
            x1 = (int)(vector.x * TrigMathUtil.cos(radAngle) + vector.z * TrigMathUtil.sin(radAngle));
            z1 = (int)(-vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle));
        }
        return new Vector3i(x1, vector.y, z1);
    }
    
    @Nonnull
    public static Vector3d rotateVectorYAxis(@Nonnull final Vector3d vector, final int angle, final boolean clockwise) {
        final float radAngle = 0.017453292f * angle;
        double x1;
        double z1;
        if (clockwise) {
            x1 = vector.x * TrigMathUtil.cos(radAngle) - vector.z * TrigMathUtil.sin(radAngle);
            z1 = vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle);
        }
        else {
            x1 = vector.x * TrigMathUtil.cos(radAngle) + vector.z * TrigMathUtil.sin(radAngle);
            z1 = -vector.x * TrigMathUtil.sin(radAngle) + vector.z * TrigMathUtil.cos(radAngle);
        }
        return new Vector3d(x1, vector.y, z1);
    }
    
    public static float wrapAngle(float angle) {
        angle %= 6.2831855f;
        if (angle <= -3.1415927f) {
            angle += 6.2831855f;
        }
        else if (angle > 3.1415927f) {
            angle -= 6.2831855f;
        }
        return angle;
    }
    
    public static float lerp(final float a, final float b, final float t) {
        return lerpUnclamped(a, b, clamp(t, 0.0f, 1.0f));
    }
    
    public static float lerpUnclamped(final float a, final float b, final float t) {
        return a + t * (b - a);
    }
    
    public static double lerp(final double a, final double b, final double t) {
        return lerpUnclamped(a, b, clamp(t, 0.0, 1.0));
    }
    
    public static double lerpUnclamped(final double a, final double b, final double t) {
        return a + t * (b - a);
    }
    
    public static float shortAngleDistance(final float a, final float b) {
        final float distance = (b - a) % 6.2831855f;
        return 2.0f * distance % 6.2831855f - distance;
    }
    
    public static float lerpAngle(final float a, final float b, final float t) {
        return a + shortAngleDistance(a, b) * t;
    }
    
    public static double floorMod(final double x, final double y) {
        return x - Math.floor(x / y) * y;
    }
    
    public static double compareAngle(final double a, final double b) {
        final double diff = b - a;
        return floorMod(diff + 3.141592653589793, 6.283185307179586) - 3.141592653589793;
    }
    
    public static double percentile(@Nonnull final long[] sortedData, final double percentile) {
        if (sortedData.length == 1) {
            return (double)sortedData[0];
        }
        if (percentile >= 1.0) {
            return (double)sortedData[sortedData.length - 1];
        }
        final double position = (sortedData.length + 1) * percentile;
        final double n = percentile * (sortedData.length - 1) + 1.0;
        long left;
        long right;
        if (position >= 1.0) {
            left = sortedData[floor(n) - 1];
            right = sortedData[floor(n)];
        }
        else {
            left = sortedData[0];
            right = sortedData[1];
        }
        if (left == right) {
            return (double)left;
        }
        final double part = n - floor(n);
        return left + part * (right - left);
    }
    
    public static double distanceToLineSq(final double x, final double y, final double ax, final double ay, final double bx, final double by) {
        final double dx0 = x - ax;
        final double dy0 = y - ay;
        final double dx2 = bx - ax;
        final double dy2 = by - ay;
        return distanceToLineSq(x, y, ax, ay, bx, by, dx0, dy0, dx2, dy2);
    }
    
    public static double distanceToLineSq(final double x, final double y, final double ax, final double ay, final double bx, final double by, final double dxAx, final double dyAy, double dBxAx, double dByAy) {
        double t = dxAx * dBxAx + dyAy * dByAy;
        t /= dBxAx * dBxAx + dByAy * dByAy;
        double px = ax;
        double py = ay;
        if (t > 1.0) {
            px = bx;
            py = by;
        }
        else if (t > 0.0) {
            px = ax + t * dBxAx;
            py = ay + t * dByAy;
        }
        dBxAx = x - px;
        dByAy = y - py;
        return dBxAx * dBxAx + dByAy * dByAy;
    }
    
    public static double distanceToInfLineSq(final double x, final double y, final double ax, final double ay, final double bx, final double by) {
        final double dx0 = x - ax;
        final double dy0 = y - ay;
        final double dx2 = bx - ax;
        final double dy2 = by - ay;
        return distanceToInfLineSq(x, y, ax, ay, dx0, dy0, dx2, dy2);
    }
    
    public static double distanceToInfLineSq(final double x, final double y, final double ax, final double ay, final double dxAx, final double dyAy, double dBxAx, double dByAy) {
        double t = dxAx * dBxAx + dyAy * dByAy;
        t /= dBxAx * dBxAx + dByAy * dByAy;
        final double px = ax + t * dBxAx;
        final double py = ay + t * dByAy;
        dBxAx = x - px;
        dByAy = y - py;
        return dBxAx * dBxAx + dByAy * dByAy;
    }
    
    public static int sideOfLine(final double x, final double y, final double ax, final double ay, final double bx, final double by) {
        return ((ax - x) * (by - y) - (ay - y) * (bx - x) >= 0.0) ? 1 : -1;
    }
    
    public static Vector3f getRotationForHitNormal(final Vector3f normal) {
        if (normal == null) {
            return Vector3f.ZERO;
        }
        if (normal.y == 1.0f) {
            return Vector3f.ZERO;
        }
        if (normal.y == -1.0f) {
            return new Vector3f(0.0f, 0.0f, 3.1415927f);
        }
        if (normal.x == 1.0f) {
            return new Vector3f(0.0f, 0.0f, -1.5707964f);
        }
        if (normal.x == -1.0f) {
            return new Vector3f(0.0f, 0.0f, 1.5707964f);
        }
        if (normal.z == 1.0f) {
            return new Vector3f(1.5707964f, 0.0f, 0.0f);
        }
        if (normal.z == -1.0f) {
            return new Vector3f(-1.5707964f, 0.0f, 0.0f);
        }
        return Vector3f.ZERO;
    }
    
    public static String getNameForHitNormal(final Vector3f normal) {
        if (normal == null) {
            return "UP";
        }
        if (normal.y == 1.0f) {
            return "UP";
        }
        if (normal.y == -1.0f) {
            return "DOWN";
        }
        if (normal.x == 1.0f) {
            return "WEST";
        }
        if (normal.x == -1.0f) {
            return "EAST";
        }
        if (normal.z == 1.0f) {
            return "NORTH";
        }
        if (normal.z == -1.0f) {
            return "SOUTH";
        }
        return "UP";
    }
    
    public static float mapToRange(final float value, final float valueMin, final float valueMax, final float rangeMin, final float rangeMax) {
        final float alpha = (value - valueMin) / (valueMax - valueMin);
        return rangeMin + alpha * (rangeMax - rangeMin);
    }
    
    static {
        EPSILON_DOUBLE = Math.ulp(1.0);
        EPSILON_FLOAT = Math.ulp(1.0f);
        MathUtil.PITCH_EDGE_PADDING = 0.01f;
    }
}
