/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.arrays;

public final class ArrayAllocator {
   
    private ArrayAllocator() {
        // utility class
    }
    
    public static <A> A forComponent(Class<?> component, int size) {
        return NativeArray.allocate(component, size).$array();
    }
    
    /**
     * Creates a new array using a the given one as prototype.
     *
     * <P>This method returns a new array of the given length whose element are
     * of the same class as of those of <code>prototype</code>. 
     * In case of an empty array, it tries to return {@link #EMPTY_ARRAY}, if possible.
     *
     * @param prototype an array that will be used to type the new one.
     * @param length the length of the new array.
     * @return a new array of given type and length.
     */
    public static <A> A forPrototype(A prototype, int length) {
        return NativeArray.wrap(prototype).allocate(length).$array();
    }

    /**
     * Ensures that an array can contain the given number of entries.
     *
     * <P>If you cannot foresee whether this array will need again to be
     * enlarged, you should probably use
     * <code>grow()</code> instead.
     *
     * @param array an array.
     * @param length the new minimum length for this array.
     * @return <code>array</code>, if it contains <code>length</code> entries or
     * more; otherwise, an array with <code>length</code> entries whose * *
     * first <code>array.length</code> entries are the same as those * *
     * of <code>array</code>.
     */
    public static <A> A ensureCapacity(A array, int length) {
        return NativeArrayAllocator.ensureCapacity(NativeArray.wrap(array), length).$array();
    }
   
    /**
     * Ensures that an array can contain the given number of entries, preserving
     * just a part of the array.
     *
     * @param array an array.
     * @param length the new minimum length for this array.
     * @param preserve the number of elements of the array that must be
     * preserved in case a new allocation is necessary.
     * @return <code>array</code>, if it can contain <code>length</code> entries
     * or more; otherwise, an array with <code>length</code> entries whose *
     * first <code>preserve</code> entries are the same as those * *
     * of <code>array</code>.
     */
    public static <A> A ensureCapacity(A array, int length, int preserve) {
        return NativeArrayAllocator.ensureCapacity(NativeArray.wrap(array), length, preserve).$array();
    }

    /**
     * Ensures that a range given by an offset and a length fits an array.
     *
     * <P>This method may be used whenever an array range check is needed.
     *
     * @param array an array.
     * @param offset a start index.
     * @param length a length (the number of elements in the range).
     * @throws IllegalArgumentException if <code>length</code> is negative.
     * @throws ArrayIndexOutOfBoundsException if <code>offset</code> is negative
     * or <code>offset</code>+<code>length</code> is greater than the array
     * length.
     */
    public static <A> void ensureOffsetLength(A array, int offset, int length) {
        NativeArrayAllocator.ensureOffsetLength(NativeArray.wrap(array), offset, length);
    }
    
    /**
     * Ensures that a range given by its first (inclusive) and last (exclusive)
     * elements fits an array.
     *
     * <P>This method may be used whenever an array range check is needed.
     *
     * @param array an array.
     * @param begin a start index (inclusive).
     * @param end an end index (exclusive).
     * @throws IllegalArgumentException if <code>from</code> is greater * *
     * than <code>to</code>.
     * @throws ArrayIndexOutOfBoundsException if <code>from</code> * *
     * or <code>to</code> are greater than the array length or negative.
     */
    public static <A> void ensureFromTo(A array, int begin, int end) {
        NativeArrayAllocator.ensureFromTo(NativeArray.wrap(array), begin, end);
    }
    
    public static <A> A resize(A array, int size) {
        return NativeArrayAllocator.resize(NativeArray.wrap(array), size).$array();
    }
    
    /**
     * Grows the given array to the maximum between the given length and the
     * current length multiplied by two, provided that the given length is
     * larger than the current length.
     *
     * <P>If you want complete control on the array growth, you should probably
     * use
     * <code>ensureCapacity()</code> instead.
     *
     * @param array an array.
     * @param length the new minimum length for this array.
     * @return <code>array</code>, if it can contain <code>length</code>
     * entries; otherwise, an array with
     * max(<code>length</code>,<code>array.length</code>/&phi;) entries whose
     * first <code>array.length</code> entries are the same as those * *
     * of <code>array</code>.
     *
     */
    public static <A> A grow(A array, int length) {
        return NativeArrayAllocator.grow(NativeArray.wrap(array), length).$array();
    }
    
    /**
     * Grows the given array to the maximum between the given length and the
     * current length multiplied by two, provided that the given length is
     * larger than the current length, preserving just a part of the array.
     *
     * <P>If you want complete control on the array growth, you should probably
     * use
     * <code>ensureCapacity()</code> instead.
     *
     * @param array an array.
     * @param length the new minimum length for this array.
     * @param preserve the number of elements of the array that must be
     * preserved in case a new allocation is necessary.
     * @return <code>array</code>, if it can contain <code>length</code>
     * entries; otherwise, an array with
     * max(<code>length</code>,<code>array.length</code>/&phi;) entries whose
     * first <code>preserve</code> entries are the same as those
     * of <code>array</code>.
     *
     */
    public static <A> A grow(A array, int length, int preserve) {
        return NativeArrayAllocator.grow(NativeArray.wrap(array), length, preserve).$array();
    }
    
    /**
     * Trims the given array to the given length.
     *
     * @param array an array.
     * @param length the new maximum length for the array.
     * @return <code>array</code>, if it contains <code>length</code> entries or
     * less; otherwise, an array with <code>length</code> entries whose entries
     * are the same as the first <code>length</code> entries * *
     * of <code>array</code>.
     *
     */
    public static <A> A trim(A array, int length) {
        return NativeArrayAllocator.trim(NativeArray.wrap(array), length).$array();
    }
    
}
