/*
 * @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.util.Arrays;
import java.util.List;
import net.ranides.assira.collection.IntComparator;
import net.ranides.assira.collection.lists.IntList;
import net.ranides.assira.collection.lists.IntRange;
import net.ranides.assira.collection.suite.IteratorTester;
import org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class ArrayUtilsTest {

    private static final IntComparator CMP = IntComparator.ASC;

    @Test
    public void testHead() {
        assertArrayEquals(array(0,0), ArrayUtils.head(array(1,5), 0, CMP));
        assertArrayEquals(array(0,0), ArrayUtils.head(array(1,5), 1, CMP));
        assertArrayEquals(array(1,2), ArrayUtils.head(array(1,5), 2, CMP));
        assertArrayEquals(array(1,3), ArrayUtils.head(array(1,5), 3, CMP));
        assertArrayEquals(array(1,4), ArrayUtils.head(array(1,5), 4, CMP));
        assertArrayEquals(array(1,5), ArrayUtils.head(array(1,5), 5, CMP));
        assertArrayEquals(array(1,5), ArrayUtils.head(array(1,5), 8, CMP));
        
        assertArrayEquals(array(0,0), ArrayUtils.head(array(1,5), 0));
        assertArrayEquals(array(1,2), ArrayUtils.head(array(1,5), 1));
        assertArrayEquals(array(1,3), ArrayUtils.head(array(1,5), 2));
        assertArrayEquals(array(1,4), ArrayUtils.head(array(1,5), 3));
        assertArrayEquals(array(1,5), ArrayUtils.head(array(1,5), 4));
        assertThrows(IndexOutOfBoundsException.class, ()->{
            ArrayUtils.head(array(1,5), 8);
        });
    }

    @Test
    public void testTail() {
        assertArrayEquals(array(1,5), ArrayUtils.tail(array(1,5), 0, CMP));
        assertArrayEquals(array(1,5), ArrayUtils.tail(array(1,5), 1, CMP));
        assertArrayEquals(array(2,5), ArrayUtils.tail(array(1,5), 2, CMP));
        assertArrayEquals(array(3,5), ArrayUtils.tail(array(1,5), 3, CMP));
        assertArrayEquals(array(4,5), ArrayUtils.tail(array(1,5), 4, CMP));
        assertArrayEquals(array(0,0), ArrayUtils.tail(array(1,5), 5, CMP));
        assertArrayEquals(array(0,0), ArrayUtils.tail(array(1,5), 8, CMP));
        
        assertArrayEquals(array(1,5), ArrayUtils.tail(array(1,5), 0));
        assertArrayEquals(array(2,5), ArrayUtils.tail(array(1,5), 1));
        assertArrayEquals(array(3,5), ArrayUtils.tail(array(1,5), 2));
        assertArrayEquals(array(4,5), ArrayUtils.tail(array(1,5), 3));
        assertArrayEquals(array(5,5), ArrayUtils.tail(array(1,5), 4));
        assertThrows(RuntimeException.class, ()->{
            ArrayUtils.tail(array(1,5), 8);
        });
    }

    @Test
    public void testSlice() {
        assertArrayEquals(array(1,7), ArrayUtils.slice(array(1,7), 0, 7, CMP));
        assertArrayEquals(array(1,7), ArrayUtils.slice(array(1,7), 1, 7, CMP));
        assertArrayEquals(array(2,7), ArrayUtils.slice(array(1,7), 2, 7, CMP));
        assertArrayEquals(array(2,6), ArrayUtils.slice(array(1,7), 2, 6, CMP));
        assertArrayEquals(array(2,4), ArrayUtils.slice(array(1,7), 2, 4, CMP));
        assertArrayEquals(array(3,4), ArrayUtils.slice(array(1,7), 3, 4, CMP));
        assertArrayEquals(array(0,0), ArrayUtils.slice(array(1,7), 3, 2, CMP));
    }
    
    private static int[] array(int begin, int end) {
        int n = end-begin;
        int res[] = new int[n];
        for(int i=0; i<n; i++) {
            res[i] = begin+i;
        }
        return res;
    }
    
    @Test
    public void testCollect() {
        List<String> input = Arrays.asList("a","b","c","d");
        
        String[] out1 = new String[4];
        assertEquals(4, ArrayUtils.collect(input.iterator(), out1));
        assertArrayEquals(new String[]{"a","b","c","d"}, out1);
        
        String[] out2 = new String[]{".",".",".",".", "e", "f"};
        assertEquals(4, ArrayUtils.collect(input.iterator(), out2));
        assertArrayEquals(new String[]{"a","b","c","d","e","f"}, out2);
    }
    
    @Test
    public void testCollect_int() {
        IntList input = new IntRange(1, 5);
        
        int[] out1 = new int[4];
        assertEquals(4, ArrayUtils.collect(input.iterator(), out1));
        assertArrayEquals(new int[]{1,2,3,4}, out1);
        
        int[] out2 = new int[]{7,7,7,7,8,9};
        assertEquals(4, ArrayUtils.collect(input.iterator(), out2));
        assertArrayEquals(new int[]{1,2,3,4,8,9}, out2);
    }
    
    @Test
    public void testCollect_bounds() {
        List<String> inp1 = Arrays.asList("1","2","3","4");
        
        String[] out1 = new String[]{"7","7","7","7","8","9"};
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out1, 1, 4));
        assertArrayEquals(new String[]{"7","1","2","3","4","9"}, out1);
        
        String[] out2 = new String[]{"7","7","7","7","8","9"};
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out2, 2, 4));
        assertArrayEquals(new String[]{"7","7","1","2","3","4"}, out2);
        
        String[] out3 = new String[]{"7","7","7","7","8","9"};
        assertEquals(3, ArrayUtils.collect(inp1.iterator(), out3, 2, 3));
        assertArrayEquals(new String[]{"7","7","1","2","3","9"}, out3);
        
        String[] out4 = new String[]{"7","7","7","7","7","7","7","7"};
        
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out4, 1, 6));
        assertArrayEquals(new String[]{"7","1","2","3","4","7","7","7"}, out4);
        
        String[] out5 = new String[]{"7","7"};
        assertEquals(2, ArrayUtils.collect(inp1.iterator(), out5, 0, 2));
        assertArrayEquals(new String[]{"1","2"}, out5);
        
        String[] out6 = new String[]{"7","7"};
        assertEquals(0, ArrayUtils.collect(inp1.iterator(), out6, 0, 0));
        assertArrayEquals(new String[]{"7","7"}, out6);
        
        String[] out7 = new String[]{"7","7","7","7","7","7"};
                
        assertThrows(IndexOutOfBoundsException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, -2, 4);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, 0, -1);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, 0, 20);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out6, 20, 1);
        });
    }
    
    @Test
    public void testCollect_int_bounds() {
        IntList inp1 = new IntRange(1, 5);
        
        int[] out1 = new int[]{7,7,7,7,8,9};
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out1, 1, 4));
        assertArrayEquals(new int[]{7,1,2,3,4,9}, out1);
        
        int[] out2 = new int[]{7,7,7,7,8,9};
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out2, 2, 4));
        assertArrayEquals(new int[]{7,7,1,2,3,4}, out2);
        
        int[] out3 = new int[]{7,7,7,7,8,9};
        assertEquals(3, ArrayUtils.collect(inp1.iterator(), out3, 2, 3));
        assertArrayEquals(new int[]{7,7,1,2,3,9}, out3);
        
        int[] out4 = new int[]{7,7,7,7,7,7,7,7};
        
        assertEquals(4, ArrayUtils.collect(inp1.iterator(), out4, 1, 6));
        assertArrayEquals(new int[]{7,1,2,3,4,7,7,7}, out4);
        
        int[] out5 = new int[]{7,7};
        assertEquals(2, ArrayUtils.collect(inp1.iterator(), out5, 0, 2));
        assertArrayEquals(new int[]{1,2}, out5);
        
        int[] out6 = new int[]{7,7};
        assertEquals(0, ArrayUtils.collect(inp1.iterator(), out6, 0, 0));
        assertArrayEquals(new int[]{7,7}, out6);
        
        int[] out7 = new int[]{7,7,7,7,7,7};
                
        assertThrows(IndexOutOfBoundsException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, -2, 4);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, 0, -1);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out7, 0, 20);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            ArrayUtils.collect(inp1.iterator(), out6, 20, 1);
        });
    }
    
    
    @Test
    public void testReverse() {
        int[] inp1 = new int[]{};
        int[] inp2 = new int[]{1};
        int[] inp3 = new int[]{1,2};
        int[] inp4 = new int[]{1,2,3};
        int[] inp5 = new int[]{1,2,3,4};
        
        assertReverse(new int[]{}, inp1);
        assertReverse(new int[]{1}, inp2);
        assertReverse(new int[]{2,1}, inp3);
        assertReverse(new int[]{3,2,1}, inp4);
        assertReverse(new int[]{4,3,2,1}, inp5);
    }
    
    @Test
    public void testReverseRange() {
        assertArrayEquals(new int[]{1,2,3,4,5,6,7}, ArrayUtils.reverse(new int[]{1,6,5,4,3,2,7}, 1, 6));
    }
    
    private void assertReverse(int[] expected, int[] values) {
        assertSame(values, ArrayUtils.reverse(values));
        assertArrayEquals(expected, values);
    }
    
    @Test
    public void testEquals() {
        
        assertFalse( ArrayUtils.equals(new int[1], new int[2]) );
        
        assertTrue( ArrayUtils.equals(new int[0], new int[0]) );
        assertTrue( ArrayUtils.equals(new int[]{1}, new int[]{1}) );
        assertTrue( ArrayUtils.equals(new int[]{1,2,3}, new int[]{1,2,3}) );
        
        assertFalse( ArrayUtils.equals(new int[]{1,2,3}, new int[]{1,2,4}) );
        assertFalse( ArrayUtils.equals(new int[]{1,2,3}, new int[]{1,4,3}) );
        assertFalse( ArrayUtils.equals(new int[]{1,2,3}, new int[]{5,2,3}) );
    }
    
    @Test
    public void testFill_FT() {
        int[] out1 = new int[4];
        int[] out2 = new int[4];
        
        ArrayUtils.fill(out1, 1, 3, 9);
        assertArrayEquals(new int[]{0, 9, 9, 0}, out1);
        
        ArrayUtils.fill(out2, 0, 3, 9);
        assertArrayEquals(new int[]{9, 9, 9, 0}, out2);
    }
    
    @Test
    public void testCopy() {
        int[] v1 = new int[0];
        int[] v2 = new int[]{1,2,3,4,5};
        String[] v3 = new String[]{};
        String[] v4 = new String[]{"a","b","c","d","e"};
        
        assertArrayEquals(v1, ArrayUtils.copy(v1));
        assertArrayEquals(v2, ArrayUtils.copy(v2));
        assertArrayEquals(v3, ArrayUtils.copy(v3));
        assertArrayEquals(v4, ArrayUtils.copy(v4));
        
        assertNotSame(v1, ArrayUtils.copy(v1));
        assertNotSame(v2, ArrayUtils.copy(v2));
        assertNotSame(v3, ArrayUtils.copy(v3));
        assertNotSame(v4, ArrayUtils.copy(v4));
        
        assertArrayEquals(new int[]{0,0,2,3,4,0}, ArrayUtils.copy(v2,1,new int[6], 2, 3));
        assertArrayEquals(new int[]{0,0,2,3,4,5,0}, ArrayUtils.copy(v2,1,new int[7], 2, 4));
        assertArrayEquals(new int[]{0,0,0}, ArrayUtils.copy(v2,1,new int[3],1,0));
        
        assertArrayEquals(new String[]{null,null,"b","c","d",null}, ArrayUtils.copy(v4,1,new String[6], 2, 3));
        assertArrayEquals(new String[]{null,null,"b","c","d","e",null}, ArrayUtils.copy(v4,1,new String[7], 2, 4));
        assertArrayEquals(new String[]{null,null,null}, ArrayUtils.copy(v4,1,new String[3],1,0));
    }
    
    @Test
    public void testRotate() {
        int v1[] = new int[]{1,2,3,4,5,6,7,8};
        int v2[] = new int[]{1,2,3,4,5,6,7,8};
        assertArrayEquals(new int[]{7,8,1,2,3,4,5,6}, ArrayUtils.rotate(v1, 2));
        assertArrayEquals(new int[]{3,4,5,6,7,8,1,2}, ArrayUtils.rotate(v2, -2));
        
        String[] v3 = new String[]{"1","2","3","4","5","6","7","8"};
        String[] v4 = new String[]{"1","2","3","4","5","6","7","8"};
        assertArrayEquals(new String[]{"7","8","1","2","3","4","5","6"}, ArrayUtils.rotate(v3, 2));
        assertArrayEquals(new String[]{"3","4","5","6","7","8","1","2"}, ArrayUtils.rotate(v4, -2));
    }
    
    @Test
    public void testIterator() {
        String[] v1 = new String[]{"1","2","3","4","5","6","7","8"};
        IteratorTester.listIterator(ArrayUtils.iterator(v1), 0, v1);
        
        IteratorTester.listIterator(ArrayUtils.iterator(v1, 2, 7), 0, new String[]{"3","4","5","6","7"});
    }
    
    @Test
    public void testSize() {
        assertEquals(0, ArrayUtils.size(null));
        assertEquals(0, ArrayUtils.size(new int[0]));
        assertEquals(2, ArrayUtils.size(new int[2]));
    }
    
}
