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

import java.lang.reflect.Array;
import net.ranides.assira.collection.mockup.TPoint;
import static net.ranides.assira.junit.NewAssert.assertThrows;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class ArrayAllocatorTest {
    
    @Test
    public void testEnsureCapacity() {
        Integer[] array1 = new Integer[]{ 1,2,3,4,5,6 };
        
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 3));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 6));
        
        Integer[] array2 = ArrayAllocator.ensureCapacity(array1, 8);
        assertNotSame(array1, array2);
        assertArrayEquals(new Integer[]{1,2,3,4,5,6,null,null}, array2);
    }
    
    @Test
    public void testEnsureCapacity_native() {
        byte[] array1 = new byte[]{ 1,2,3,4,5,6 };
        
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 3));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 6));
        
        byte[] array2 = ArrayAllocator.ensureCapacity(array1, 8);
        assertNotSame(array1, array2);
        assertArrayEquals(new byte[]{1,2,3,4,5,6,0,0}, array2);
    }

    @Test
    public void testEnsureCapacity_Preserve() {
        Integer[] array1 = new Integer[]{ 1,2,3,4,5,6 };
        
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 0, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 3, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 6, 0));
        
        Integer[] array2 = ArrayAllocator.ensureCapacity(array1, 8, 6);
        assertNotSame(array1, array2);
        assertArrayEquals(new Integer[]{1,2,3,4,5,6,null,null}, array2);
        
        Integer[] array3 = ArrayAllocator.ensureCapacity(array1, 8, 4);
        assertNotSame(array1, array3);
        assertArrayEquals(new Integer[]{1,2,3,4,null,null,null,null}, array3);
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureCapacity(array1, 8, 7);
        });
    }
    
    @Test
    public void testEnsureCapacity_Preserve_native() {
        byte[] array1 = new byte[]{ 1,2,3,4,5,6 };
        
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 0, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 3, 0));
        assertSame(array1, ArrayAllocator.ensureCapacity(array1, 6, 0));
        
        byte[] array2 = ArrayAllocator.ensureCapacity(array1, 8, 6);
        assertNotSame(array1, array2);
        assertArrayEquals(new byte[]{1,2,3,4,5,6,0,0}, array2);
        
        byte[] array3 = ArrayAllocator.ensureCapacity(array1, 8, 4);
        assertNotSame(array1, array3);
        assertArrayEquals(new byte[]{1,2,3,4,0,0,0,0}, array3);
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureCapacity(array1, 8, 7);
        });
    }

    @Test
    public void testEnsureOffsetLength() {
        Integer[] array1 = new Integer[4];
        ArrayAllocator.ensureOffsetLength(array1, 1, 2);
        ArrayAllocator.ensureOffsetLength(array1, 0, 4);
        ArrayAllocator.ensureOffsetLength(array1, 1, 3);
        
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 1, -1);
        });
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 8, -1);
        });
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, -1, 1);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 8, 1);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 3, 10);
        });
    }
   
    @Test
    public void testEnsureOffsetLength_native() {
        byte[] array1 = new byte[4];
        ArrayAllocator.ensureOffsetLength(array1, 1, 2);
        ArrayAllocator.ensureOffsetLength(array1, 0, 4);
        ArrayAllocator.ensureOffsetLength(array1, 1, 3);
        
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 1, -1);
        });
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 8, -1);
        });
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, -1, 1);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 8, 1);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureOffsetLength(array1, 3, 10);
        });
    }

    @Test
    public void testEnsureFromTo() {
        Integer[] array1 = new Integer[4];
        ArrayAllocator.ensureFromTo(array1, 1, 3);
        ArrayAllocator.ensureFromTo(array1, 0, 4);
        ArrayAllocator.ensureFromTo(array1, 1, 4);
        
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 3, 1);
        });
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, -1, 8);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 2, 8);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 5, 6);
        });
    }
    
    @Test
    public void testEnsureFromTo_native() {
        byte[] array1 = new byte[4];
        ArrayAllocator.ensureFromTo(array1, 1, 3);
        ArrayAllocator.ensureFromTo(array1, 0, 4);
        ArrayAllocator.ensureFromTo(array1, 1, 4);
        
        assertThrows(IllegalArgumentException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 3, 1);
        });
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, -1, 8);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 2, 8);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()-> {
            ArrayAllocator.ensureFromTo(array1, 5, 6);
        });
    }
    
    @Test
    public void testGrow() {
        Integer[] array1 = new Integer[]{1,2,3};
        assertSame(array1, ArrayAllocator.grow(array1, 2));
        assertSame(array1, ArrayAllocator.grow(array1, 3));
        assertArrayEquals(new Integer[]{1,2,3,null,null,null}, ArrayAllocator.grow(array1, 4));
        assertArrayEquals(new Integer[]{1,2,3,null,null,null}, ArrayAllocator.grow(array1, 5));
        assertArrayEquals(new Integer[]{1,2,3,null,null,null}, ArrayAllocator.grow(array1, 6));
        assertArrayEquals(new Integer[]{1,2,3,null,null,null,null}, ArrayAllocator.grow(array1, 7));
        assertArrayEquals(new Integer[]{1,2,3,null,null,null,null,null,null}, ArrayAllocator.grow(array1, 9));
    }
    
    @Test
    public void testGrow_native() {
        byte[] array1 = new byte[]{1,2,3};
        assertSame(array1, ArrayAllocator.grow(array1, 2));
        assertSame(array1, ArrayAllocator.grow(array1, 3));
        assertArrayEquals(new byte[]{1,2,3,0,0,0}, ArrayAllocator.grow(array1, 4));
        assertArrayEquals(new byte[]{1,2,3,0,0,0}, ArrayAllocator.grow(array1, 5));
        assertArrayEquals(new byte[]{1,2,3,0,0,0}, ArrayAllocator.grow(array1, 6));
        assertArrayEquals(new byte[]{1,2,3,0,0,0,0}, ArrayAllocator.grow(array1, 7));
        assertArrayEquals(new byte[]{1,2,3,0,0,0,0,0,0}, ArrayAllocator.grow(array1, 9));
    }
    
    @Test
    public void testGrow_preserve() {
        Integer[] array1 = new Integer[]{1,2,3};
        assertSame(array1, ArrayAllocator.grow(array1, 2, 2));
        assertSame(array1, ArrayAllocator.grow(array1, 3, 2));
        assertArrayEquals(new Integer[]{1,2,null,null,null,null}, ArrayAllocator.grow(array1, 4, 2));
        assertArrayEquals(new Integer[]{1,2,null,null,null,null}, ArrayAllocator.grow(array1, 5, 2));
        assertArrayEquals(new Integer[]{1,2,null,null,null,null}, ArrayAllocator.grow(array1, 6, 2));
        assertArrayEquals(new Integer[]{1,2,null,null,null,null,null}, ArrayAllocator.grow(array1, 7, 2));
        assertArrayEquals(new Integer[]{1,2,null,null,null,null,null,null,null}, ArrayAllocator.grow(array1, 9, 2));
    }
    
    @Test
    public void testGrow_preserve_native() {
        byte[] array1 = new byte[]{1,2,3};
        assertSame(array1, ArrayAllocator.grow(array1, 2, 2));
        assertSame(array1, ArrayAllocator.grow(array1, 3, 2));
        assertArrayEquals(new byte[]{1,2,0,0,0,0}, ArrayAllocator.grow(array1, 4, 2));
        assertArrayEquals(new byte[]{1,2,0,0,0,0}, ArrayAllocator.grow(array1, 5, 2));
        assertArrayEquals(new byte[]{1,2,0,0,0,0}, ArrayAllocator.grow(array1, 6, 2));
        assertArrayEquals(new byte[]{1,2,0,0,0,0,0}, ArrayAllocator.grow(array1, 7, 2));
        assertArrayEquals(new byte[]{1,2,0,0,0,0,0,0,0}, ArrayAllocator.grow(array1, 9, 2));
    }
    
    @Test
    public void testTrim() {
        Integer[] array1 = new Integer[]{1,2,3};
        
        assertSame(array1, ArrayAllocator.trim(array1, 5));
        assertSame(array1, ArrayAllocator.trim(array1, 3));
        assertArrayEquals(new Integer[]{1,2}, ArrayAllocator.trim(array1, 2));
        assertArrayEquals(new Integer[]{1}, ArrayAllocator.trim(array1, 1));
        assertArrayEquals(new Integer[]{}, ArrayAllocator.trim(array1, 0));
    }
        
    @Test
    public void testTrim_native() {
        byte[] array1 = new byte[]{1,2,3};
        
        assertSame(array1, ArrayAllocator.trim(array1, 5));
        assertSame(array1, ArrayAllocator.trim(array1, 3));
        assertArrayEquals(new byte[]{1,2}, ArrayAllocator.trim(array1, 2));
        assertArrayEquals(new byte[]{1}, ArrayAllocator.trim(array1, 1));
        assertArrayEquals(new byte[]{}, ArrayAllocator.trim(array1, 0));
    }
    
    @Test
    public void testForPrototype() {
        assertArray(Float[].class, 4, ArrayAllocator.forPrototype(new Float[0], 4));
        assertArray(Integer[].class, 4, ArrayAllocator.forPrototype(new Integer[0], 4));
        
        assertArray(Float[].class, 0, ArrayAllocator.forPrototype(new Float[0], 0));
        
        assertArray(Object[].class, 4, ArrayAllocator.forPrototype(new Object[0], 4));
        
        assertArray(Object[].class, 0, ArrayAllocator.forPrototype(new Object[0], 0));
    }
    
    @Test
    public void testForPrototype_native() {
        assertArray(float[].class, 4, ArrayAllocator.forPrototype(new float[0], 4));
        assertArray(int[].class, 4, ArrayAllocator.forPrototype(new int[0], 4));
        assertArray(float[].class, 0, ArrayAllocator.forPrototype(new float[0], 0));
    }
    
    @Test
    public void testForComponent() {
        assertArray(Float[].class, 4, ArrayAllocator.forComponent(Float.class, 4));
        assertArray(Integer[].class, 4, ArrayAllocator.forComponent(Integer.class, 4));
        assertArray(Object[].class, 4, ArrayAllocator.forComponent(Object.class, 4));
        assertArray(TPoint[].class, 4, ArrayAllocator.forComponent(TPoint.class, 4));
        
        assertArray(Float[].class, 0, ArrayAllocator.forComponent(Float.class, 0));
        assertArray(Integer[].class, 0, ArrayAllocator.forComponent(Integer.class, 0));
        assertArray(Object[].class, 0, ArrayAllocator.forComponent(Object.class, 0));
        assertArray(TPoint[].class, 0, ArrayAllocator.forComponent(TPoint.class, 0));
    }
    
    @Test
    public void testForComponent_native() {
        assertArray(byte[].class, 0, ArrayAllocator.forComponent(byte.class, 0));
        assertArray(short[].class, 0, ArrayAllocator.forComponent(short.class, 0));
        assertArray(int[].class, 0, ArrayAllocator.forComponent(int.class, 0));
        assertArray(long[].class, 0, ArrayAllocator.forComponent(long.class, 0));
        assertArray(float[].class, 0, ArrayAllocator.forComponent(float.class, 0));
        assertArray(double[].class, 0, ArrayAllocator.forComponent(double.class, 0));
        assertArray(boolean[].class, 0, ArrayAllocator.forComponent(boolean.class, 0));
        assertArray(char[].class, 0, ArrayAllocator.forComponent(char.class, 0));
        
        assertArray(byte[].class, 4, ArrayAllocator.forComponent(byte.class, 4));
        assertArray(short[].class, 4, ArrayAllocator.forComponent(short.class, 4));
        assertArray(int[].class, 4, ArrayAllocator.forComponent(int.class, 4));
        assertArray(long[].class, 4, ArrayAllocator.forComponent(long.class, 4));
        assertArray(float[].class, 4, ArrayAllocator.forComponent(float.class, 4));
        assertArray(double[].class, 4, ArrayAllocator.forComponent(double.class, 4));
        assertArray(boolean[].class, 4, ArrayAllocator.forComponent(boolean.class, 4));
        assertArray(char[].class, 4, ArrayAllocator.forComponent(char.class, 4));
    }
	
	@Test
	public void testResize() {
		Integer[] a1 = new Integer[]{1,2,3};
		Integer[] o1 = ArrayAllocator.resize(a1, 0);
		Integer[] o2 = ArrayAllocator.resize(a1, 1);
		Integer[] o3 = ArrayAllocator.resize(a1, 3);
		Integer[] o4 = ArrayAllocator.resize(a1, 5);
		assertArrayEquals(new Integer[0], o1);
		assertArrayEquals(new Integer[]{1}, o2);
		assertSame(a1, o3);
		assertArrayEquals(new Integer[]{1,2,3,null,null}, o4);
	}
	
	@Test
	public void testResize_native() {
		int[] a1 = new int[]{1,2,3};
		int[] o1 = ArrayAllocator.resize(a1, 0);
		int[] o2 = ArrayAllocator.resize(a1, 1);
		int[] o3 = ArrayAllocator.resize(a1, 3);
		int[] o4 = ArrayAllocator.resize(a1, 5);
		assertArrayEquals(new int[0], o1);
		assertArrayEquals(new int[]{1}, o2);
		assertSame(a1, o3);
		assertArrayEquals(new int[]{1,2,3,0,0}, o4);
	}
    
    private void assertArray(Class<?> type, int size, Object array) {
        assertEquals(type, array.getClass());
        assertEquals(size, Array.getLength(array));
    }
    
}
