/*
 * @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 org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class NativeArrayUtilsTest {
    
    private static final IntComparator CMP = IntComparator.ASC;
    
    @Test
    public void testHead() {
        assertEquals(array(0,0), NativeArrayUtils.head(array(1,5), 0, CMP));
        assertEquals(array(0,0), NativeArrayUtils.head(array(1,5), 1, CMP));
        assertEquals(array(1,2), NativeArrayUtils.head(array(1,5), 2, CMP));
        assertEquals(array(1,3), NativeArrayUtils.head(array(1,5), 3, CMP));
        assertEquals(array(1,4), NativeArrayUtils.head(array(1,5), 4, CMP));
        assertEquals(array(1,5), NativeArrayUtils.head(array(1,5), 5, CMP));
        assertEquals(array(1,5), NativeArrayUtils.head(array(1,5), 8, CMP));
    }

    @Test
    public void testTail() {
        assertEquals(array(1,5), NativeArrayUtils.tail(array(1,5), 0, CMP));
        assertEquals(array(1,5), NativeArrayUtils.tail(array(1,5), 1, CMP));
        assertEquals(array(2,5), NativeArrayUtils.tail(array(1,5), 2, CMP));
        assertEquals(array(3,5), NativeArrayUtils.tail(array(1,5), 3, CMP));
        assertEquals(array(4,5), NativeArrayUtils.tail(array(1,5), 4, CMP));
        assertEquals(array(0,0), NativeArrayUtils.tail(array(1,5), 5, CMP));
        assertEquals(array(0,0), NativeArrayUtils.tail(array(1,5), 8, CMP));
    }

    @Test
    public void testSlice() {
        assertEquals(array(1,7), NativeArrayUtils.slice(array(1,7), 0, 7, CMP));
        assertEquals(array(1,7), NativeArrayUtils.slice(array(1,7), 1, 7, CMP));
        assertEquals(array(2,7), NativeArrayUtils.slice(array(1,7), 2, 7, CMP));
        assertEquals(array(2,6), NativeArrayUtils.slice(array(1,7), 2, 6, CMP));
        assertEquals(array(2,4), NativeArrayUtils.slice(array(1,7), 2, 4, CMP));
        assertEquals(array(3,4), NativeArrayUtils.slice(array(1,7), 3, 4, CMP));
        assertEquals(array(0,0), NativeArrayUtils.slice(array(1,7), 3, 2, CMP));
    }
    
    @Test
    public void testCollect() {
        List<Integer> input = Arrays.asList(1,2,3,4);
        
        Integer[] out1 = new Integer[4];
        assertEquals(4, NativeArrayUtils.collect(input.iterator(), $a(out1)));
        assertArrayEquals(new Integer[]{1,2,3,4}, out1);
        
        Integer[] out2 = new Integer[]{7,7,7,7,8,9};
        assertEquals(4, NativeArrayUtils.collect(input.iterator(), $a(out2)));
        assertArrayEquals(new Integer[]{1,2,3,4,8,9}, out2);
        
        assertThrows(ArrayStoreException.class, ()->{
            // mamy brzydki mismatch, zrobiło nam T = Number :)
            Integer[] out3 = new Integer[]{7,7,7,7,8,9};
            List<Short> inp3 = Arrays.<Short>asList((short)1,(short)2,(short)3,(short)4);
            NativeArrayUtils.collect(inp3.iterator(), $a(out3));
        });
        
        // tutaj mamy poprawne rozpoznanie T = Number
        Number[] out4 = new Number[]{7,7,7,7,8,9L};
        List<Short> inp4 = Arrays.<Short>asList((short)1,(short)2,(short)3,(short)4);
        Number[] exp4 = new Number[]{ (short)1,(short)2,(short)3,(short)4,(int)8,9L };
        assertEquals(4, NativeArrayUtils.collect(inp4.iterator(), $a(out4)));
        assertArrayEquals(exp4, out4);
    }
    
    @Test
    public void testCollect_bounds() {
        List<Integer> input = Arrays.asList(1,2,3,4);
        
        Integer[] out1 = new Integer[]{7,7,7,7,8,9};
        assertEquals(4, NativeArrayUtils.collect(input.iterator(), $a(out1), 1, 4));
        assertArrayEquals(new Integer[]{7,1,2,3,4,9}, out1);
        
        Integer[] out2 = new Integer[]{7,7,7,7,8,9};
        assertEquals(4, NativeArrayUtils.collect(input.iterator(), $a(out2), 2, 4));
        assertArrayEquals(new Integer[]{7,7,1,2,3,4}, out2);
        
        Integer[] out3 = new Integer[]{7,7,7,7,8,9};
        assertEquals(3, NativeArrayUtils.collect(input.iterator(), $a(out3), 2, 3));
        assertArrayEquals(new Integer[]{7,7,1,2,3,9}, out3);
        
        Integer[] out4 = new Integer[]{7,7,7,7,7,7,7,7};
        
        assertEquals(4, NativeArrayUtils.collect(input.iterator(), $a(out4), 1, 6));
        assertArrayEquals(new Integer[]{7,1,2,3,4,7,7,7}, out4);
        
        Integer[] out5 = new Integer[]{7,7};
        assertEquals(2, NativeArrayUtils.collect(input.iterator(), $a(out5), 0, 2));
        assertArrayEquals(new Integer[]{1,2}, out5);
        
        Integer[] out6 = new Integer[]{7,7};
        assertEquals(0, NativeArrayUtils.collect(input.iterator(), $a(out6), 0, 0));
        assertArrayEquals(new Integer[]{7,7}, out6);
        
        Integer[] out7 = new Integer[]{7,7,7,7,7,7};
                
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            NativeArrayUtils.collect(input.iterator(), $a(out7), -2, 4);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            NativeArrayUtils.collect(input.iterator(), $a(out7), 0, -1);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            NativeArrayUtils.collect(input.iterator(), $a(out7), 0, 20);
        });
        assertThrows(IllegalArgumentException.class, ()->{
            NativeArrayUtils.collect(input.iterator(), $a(out6), 20, 1);
        });
    }
    
    @Test
    public void testReverse() {
        NativeArray inp1 = $a(new Integer[]{});
        NativeArray inp2 = $a(new Integer[]{1});
        NativeArray inp3 = $a(new Integer[]{1,2});
        NativeArray inp4 = $a(new Integer[]{1,2,3});
        NativeArray inp5 = $a(new Integer[]{1,2,3,4});
        
        assertReverse($a(new Integer[]{}), inp1);
        assertReverse($a(new Integer[]{1}), inp2);
        assertReverse($a(new Integer[]{2,1}), inp3);
        assertReverse($a(new Integer[]{3,2,1}), inp4);
        assertReverse($a(new Integer[]{4,3,2,1}), inp5);
    }
    
    private <T> void assertReverse(NativeArray expected, NativeArray values) {
        assertSame(values, NativeArrayUtils.reverse(values));
        assertEquals(expected, values);
    }
    
    @Test
    public void testEquals() {
        
        assertFalse( nae(new Integer[1], new Integer[2]) );
        
        assertTrue( nae(new Integer[0], new Integer[0]) );
        assertTrue( nae(new Integer[]{1}, new Integer[]{1}) );
        assertTrue( nae(new Integer[]{1,2,3}, new Integer[]{1,2,3}) );
        assertTrue( nae(new Integer[]{1,2,null}, new Integer[]{1,2,null}) );
        
        assertFalse( nae(new Integer[]{1,2,3}, new Integer[]{1,2,4}) );
        assertFalse( nae(new Integer[]{1,2,3}, new Integer[]{1,2,null}) );
        assertFalse( nae(new Integer[]{1,2,null}, new Integer[]{1,2,3}) );
        assertFalse( nae(new Integer[]{1,2,3}, new Integer[]{1,4,3}) );
        assertFalse( nae(new Integer[]{1,2,3}, new Integer[]{5,2,3}) );
    }
    
    private <A> boolean nae(A a, A b) {
        return NativeArrayUtils.equals($a(a), $a(b));
    }
    
    @Test
    public void testFill_FT() {
        String[] out1 = new String[4];
        String[] out2 = new String[4];
        
        NativeArrayUtils.fill($a(out1), 1, 3, "#");
        assertArrayEquals(new String[]{null, "#", "#", null}, out1);
        
        NativeArrayUtils.fill($a(out2), 0, 3, "#");
        assertArrayEquals(new String[]{"#", "#", "#", null}, out2);
    }
    
    @Test
    public void testCopy() {
        NativeArray v1 = $a(new int[0]);
        NativeArray v2 = $a(new int[]{1,2,3,4,5});
        NativeArray v3 = $a(new String[]{});
        NativeArray v4 = $a(new String[]{"a","b","c","d","e"});
        
        assertEquals(v1, NativeArrayUtils.copy(v1));
        assertEquals(v2, NativeArrayUtils.copy(v2));
        assertEquals(v3, NativeArrayUtils.copy(v3));
        assertEquals(v4, NativeArrayUtils.copy(v4));
        
        assertNotSame(v1, NativeArrayUtils.copy(v1));
        assertNotSame(v2, NativeArrayUtils.copy(v2));
        assertNotSame(v3, NativeArrayUtils.copy(v3));
        assertNotSame(v4, NativeArrayUtils.copy(v4));
        
        assertEquals($a(new int[]{0,0,2,3,4,0}), NativeArrayUtils.copy(v2,1,$a(new int[6]), 2, 3));
        assertEquals($a(new int[]{0,0,2,3,4,5,0}), NativeArrayUtils.copy(v2,1,$a(new int[7]), 2, 4));
        assertEquals($a(new int[]{0,0,0}), NativeArrayUtils.copy(v2,1,$a(new int[3]),1,0));
        
        assertEquals($a(new String[]{null,null,"b","c","d",null}), NativeArrayUtils.copy(v4,1,$a(new String[6]), 2, 3));
        assertEquals($a(new String[]{null,null,"b","c","d","e",null}), NativeArrayUtils.copy(v4,1,$a(new String[7]), 2, 4));
        assertEquals($a(new String[]{null,null,null}), NativeArrayUtils.copy(v4,1,$a(new String[3]),1,0));
    }
    
    @Test
    public void testSize() {
        assertEquals(0, NativeArrayUtils.size(array(0,0)));
        assertEquals(1, NativeArrayUtils.size(array(0,1)));
        assertEquals(2, NativeArrayUtils.size(array(0,2)));
    }
    
    private static NativeArray $a(Object array) {
        return NativeArray.wrap(array);
    }
      
    private static NativeArray array(int begin, int end) {
        int n = end-begin;
        Integer res[] = new Integer[n];
        for(int i=0; i<n; i++) {
            res[i] = begin+i;
        }
        return NativeArray.wrap(res);
    }
    
}