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

package com.hypixel.hytale.common.util;

import java.util.Objects;
import java.util.Random;
import com.hypixel.hytale.function.predicate.UnaryBiPredicate;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.function.IntFunction;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.util.MathUtil;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.function.Supplier;
import java.util.BitSet;

public class ArrayUtil
{
    public static final String[] EMPTY_STRING_ARRAY;
    public static final double[] EMPTY_DOUBLE_ARRAY;
    public static final int[] EMPTY_INT_ARRAY;
    public static final long[] EMPTY_LONG_ARRAY;
    public static final boolean[] EMPTY_BOOLEAN_ARRAY;
    public static final Integer[] EMPTY_INTEGER_ARRAY;
    public static final byte[] EMPTY_BYTE_ARRAY;
    public static final BitSet[] EMPTY_BITSET_ARRAY;
    public static final float[] EMPTY_FLOAT_ARRAY;
    private static final Object[] EMPTY_OBJECT_ARRAY;
    private static final Supplier[] EMPTY_SUPPLIER_ARRAY;
    private static final Map.Entry[] EMPTY_ENTRY_ARRAY;
    
    @Nonnull
    public static <T> T[] emptyArray() {
        return (T[])ArrayUtil.EMPTY_OBJECT_ARRAY;
    }
    
    @Nonnull
    public static <T> Supplier<T>[] emptySupplierArray() {
        return ArrayUtil.EMPTY_SUPPLIER_ARRAY;
    }
    
    @Nonnull
    public static <K, V> Map.Entry<K, V>[] emptyEntryArray() {
        return ArrayUtil.EMPTY_ENTRY_ARRAY;
    }
    
    public static int grow(final int oldSize) {
        return (int)MathUtil.clamp(oldSize + (long)(oldSize >> 1), 2L, 2147483639L);
    }
    
    public static <StartType, EndType> EndType[] copyAndMutate(@Nullable final StartType[] array, @Nonnull final Function<StartType, EndType> adapter, @Nonnull final IntFunction<EndType[]> arrayProvider) {
        if (array == null) {
            return null;
        }
        final EndType[] endArray = arrayProvider.apply(array.length);
        for (int i = 0; i < endArray.length; ++i) {
            endArray[i] = adapter.apply(array[i]);
        }
        return endArray;
    }
    
    @Nullable
    public static <T> T[] combine(@Nullable final T[] a1, @Nullable final T[] a2) {
        if (a1 == null || a1.length == 0) {
            return a2;
        }
        if (a2 == null || a2.length == 0) {
            return a1;
        }
        final T[] newArray = Arrays.copyOf(a1, a1.length + a2.length);
        System.arraycopy(a2, 0, newArray, a1.length, a2.length);
        return newArray;
    }
    
    @Nonnull
    public static <T> T[] append(@Nullable final T[] arr, @Nonnull final T t) {
        if (arr == null) {
            final T[] newArray = (T[])Array.newInstance(t.getClass(), 1);
            newArray[0] = t;
            return newArray;
        }
        final T[] newArray = Arrays.copyOf(arr, arr.length + 1);
        newArray[arr.length] = t;
        return newArray;
    }
    
    @Nonnull
    public static <T> T[] remove(@Nonnull final T[] arr, final int index) {
        final int newLength = arr.length - 1;
        final T[] newArray = (T[])Array.newInstance(arr.getClass().getComponentType(), newLength);
        System.arraycopy(arr, 0, newArray, 0, index);
        if (index < newLength) {
            System.arraycopy(arr, index + 1, newArray, index, newLength - index);
        }
        return newArray;
    }
    
    public static boolean startsWith(@Nonnull final byte[] array, @Nonnull final byte[] start) {
        if (start.length > array.length) {
            return false;
        }
        for (int i = 0; i < start.length; ++i) {
            if (array[i] != start[i]) {
                return false;
            }
        }
        return true;
    }
    
    public static <T> boolean equals(@Nullable final T[] a, @Nullable final T[] a2, @Nonnull final UnaryBiPredicate<T> predicate) {
        if (a == a2) {
            return true;
        }
        if (a == null || a2 == null) {
            return false;
        }
        final int length = a.length;
        if (a2.length != length) {
            return false;
        }
        for (int i = 0; i < length; ++i) {
            final T o1 = a[i];
            final T o2 = a2[i];
            if (o1 == null) {
                if (o2 != null) {
                    return false;
                }
            }
            else if (!predicate.test(o1, o2)) {
                return false;
            }
        }
        return true;
    }
    
    @Nonnull
    public static <T> T[][] split(@Nonnull final T[] data, final int size) {
        final Class<? extends T[]> aClass = (Class<? extends T[]>)data.getClass();
        final T[][] ret = (T[][])Array.newInstance(aClass.getComponentType(), MathUtil.ceil(data.length / (double)size), 0);
        int start = 0;
        for (int i = 0; i < ret.length; ++i) {
            ret[i] = Arrays.copyOfRange(data, start, Math.min(start + size, data.length));
            start += size;
        }
        return ret;
    }
    
    public static byte[][] split(@Nonnull final byte[] data, final int size) {
        final byte[][] ret = new byte[MathUtil.ceil(data.length / (double)size)][];
        int start = 0;
        for (int i = 0; i < ret.length; ++i) {
            ret[i] = Arrays.copyOfRange(data, start, Math.min(start + size, data.length));
            start += size;
        }
        return ret;
    }
    
    public static void shuffleArray(@Nonnull final int[] ar, final int from, final int to, @Nonnull final Random rnd) {
        Objects.checkFromToIndex(from, to, ar.length);
        for (int i = to - 1; i > from; --i) {
            final int index = rnd.nextInt(i + 1 - from) + from;
            final int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }
    
    public static void shuffleArray(@Nonnull final byte[] ar, final int from, final int to, @Nonnull final Random rnd) {
        Objects.checkFromToIndex(from, to, ar.length);
        for (int i = to - 1; i > from; --i) {
            final int index = rnd.nextInt(i + 1 - from) + from;
            final byte a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }
    
    public static <T> boolean contains(@Nonnull final T[] array, @Nullable final T obj) {
        return indexOf(array, obj) >= 0;
    }
    
    public static <T> boolean contains(@Nonnull final T[] array, @Nullable final T obj, final int start, final int end) {
        return indexOf(array, obj, start, end) >= 0;
    }
    
    public static <T> int indexOf(@Nonnull final T[] array, @Nullable final T obj) {
        return indexOf(array, obj, 0, array.length);
    }
    
    public static <T> int indexOf(@Nonnull final T[] array, @Nullable final T obj, final int start, final int end) {
        if (obj == null) {
            for (int i = start; i < end; ++i) {
                if (array[i] == null) {
                    return i;
                }
            }
        }
        else {
            for (int i = start; i < end; ++i) {
                if (obj.equals(array[i])) {
                    return i;
                }
            }
        }
        return -1;
    }
    
    static {
        EMPTY_STRING_ARRAY = new String[0];
        EMPTY_DOUBLE_ARRAY = new double[0];
        EMPTY_INT_ARRAY = new int[0];
        EMPTY_LONG_ARRAY = new long[0];
        EMPTY_BOOLEAN_ARRAY = new boolean[0];
        EMPTY_INTEGER_ARRAY = new Integer[0];
        EMPTY_BYTE_ARRAY = new byte[0];
        EMPTY_BITSET_ARRAY = new BitSet[0];
        EMPTY_FLOAT_ARRAY = new float[0];
        EMPTY_OBJECT_ARRAY = new Object[0];
        EMPTY_SUPPLIER_ARRAY = new Supplier[0];
        EMPTY_ENTRY_ARRAY = new Map.Entry[0];
    }
}
