/*
 * @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.ArrayList;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.IntPredicate;
import javax.annotation.Resource;
import net.ranides.assira.collection.IntComparator;
import net.ranides.assira.collection.arrays.NativeArray.NativeComparator;
import net.ranides.assira.collection.arrays.NativeArray.NativeMove;
import net.ranides.assira.generic.CompareUtils;
import net.ranides.assira.test.TCollection;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.junit.TestContract;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings({
    "PMD.JUnit4TestShouldUseTestAnnotation",
    "PMD.MethodNameRules"
})
public class NativeArrayTester<K> {
    
    @Resource(name = "collection!")
    private TCollection<K> $var;
   
    @TestContract
    public void testCompare(Function<int[], NativeArray> f) {
        NativeArray a = f.apply(new int[]{1,3,5,7});
        NativeArray b = f.apply(new int[]{1,3,5,7});
        NativeArray c = f.apply(new int[]{1,3,5});
        NativeArray d = f.apply(new int[]{1,2,5,7});
        NativeArray e = f.apply(new int[]{1,4,5,7});
        
        assertTrue("a==a", a.compareTo(a) == 0 );
        assertTrue("a==b", a.compareTo(b) == 0 );
        
        assertTrue("a>c", a.compareTo(c) > 0 );
        assertTrue("c<a", c.compareTo(a) < 0 );
        
        assertTrue("a==c (3)", a.compareTo(c,0,0,3) == 0 );
        assertTrue("c==a (3)", c.compareTo(a,0,0,3) == 0 );
        
        assertTrue("a==c (0)", a.compareTo(c,0,0,0) == 0 );
        assertTrue("c==a (0)", c.compareTo(a,1,0,0) == 0 );
        
        assertTrue("a>d", a.compareTo(d) > 0 );
        assertTrue("d<a", d.compareTo(a) < 0 );
        
        assertTrue("a<e", a.compareTo(e) < 0 );
        assertTrue("e>a", e.compareTo(a) > 0 );
    }
    
    @TestContract
    public void testCompare_cmp(Function<int[], NativeArray> f) {
        Comparator cmp = CompareUtils.comparator().reversed();
            
        NativeArray a = f.apply(new int[]{1,3,5,7});
        NativeArray b = f.apply(new int[]{1,3,5,7});
        NativeArray c = f.apply(new int[]{1,3,5});
        NativeArray d = f.apply(new int[]{1,2,5,7});
        NativeArray e = f.apply(new int[]{1,4,5,7});
        
        assertTrue("a==a", a.compareTo(a, cmp) == 0 );
        assertTrue("a==b", a.compareTo(b, cmp) == 0 );
        
        assertTrue("a>c", a.compareTo(c, cmp) > 0 );
        assertTrue("c<a", c.compareTo(a, cmp) < 0 );
        
        assertTrue("a==c (3)", a.compareTo(c,0,0,3, cmp) == 0 );
        assertTrue("c==a (3)", c.compareTo(a,0,0,3, cmp) == 0 );
        
        assertTrue("a==c (0)", a.compareTo(c,0,0,0, cmp) == 0 );
        assertTrue("c==a (0)", c.compareTo(a,1,0,0, cmp) == 0 );
        
        assertTrue("a<d", a.compareTo(d, cmp) < 0 );
        assertTrue("d>a", d.compareTo(a, cmp) > 0 );
        
        assertTrue("a>e", a.compareTo(e, cmp) > 0 );
        assertTrue("e<a", e.compareTo(a, cmp) < 0 );
    }
    
    @TestContract
    public void testEquals(Function<int[], NativeArray> f) {
        NativeArray a = f.apply(new int[]{1,3,5,7});
        NativeArray b = f.apply(new int[]{1,3,5,7});
        NativeArray c = f.apply(new int[]{1,3,5});
        NativeArray d = f.apply(new int[]{1,4,5,7});

        assertTrue("a==a", a.equals(a));
        assertTrue("a==b", a.equals(b));
        assertFalse("a!=c", a.equals(c));
        assertFalse("a!=d", a.equals(d));
    }

    @TestContract
    public void testSet(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7});
        target.set(0, $var.item(4).value());
        target.set(1, $var.item(6).value());
        target.set(3, $var.item(8).value());
        
        assertEquals(f.apply(new int[]{4,6,5,8}), target);
        
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.set(4, $var.item(9).value());
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.set(9, $var.item(9).value());
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.set(-1, $var.item(9).value());
        });
        
        target.set(1,3, $var.item(2).value());
        assertEquals(f.apply(new int[]{4,2,2,8}), target);
    }
    
    @TestContract
    public void testMove(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7});
        
        target.move(0,1);
        target.move(1,3);
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.move(0,5);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.move(5,0);
        });
        
        assertEquals(f.apply(new int[]{3,7,5,7}), target);
        
        NativeMove imov = target.move(f.apply(new int[]{11,12,13,14}));
        imov.move(0, 2);
        imov.move(1, 1);
        imov.move(3, 0);
        assertEquals(f.apply(new int[]{13,12,5,11}), target);
    }
    
    @TestContract
    public void testSwap(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7,2,4});
        
        target.swap(0,1);
        target.swap(1,3);
        target.swap(2,5);
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.move(0,6);
        });
        assertThrows(ArrayIndexOutOfBoundsException.class, ()->{
            target.move(6,0);
        });
        
        assertEquals(f.apply(new int[]{3,7,4,1,2,5}), target);
        
    }
    
    @TestContract
    public void testLRCompare(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7});
        
        NativeComparator lcmp = target.lcmp($var.item(4).value());
        NativeComparator rcmp = target.rcmp($var.item(4).value());
        
        assertEquals($cmp(4,1), lcmp.compare(0));
        assertEquals($cmp(4,3), lcmp.compare(1));
        assertEquals($cmp(4,5), lcmp.compare(2));
        assertEquals($cmp(4,7), lcmp.compare(3));
        
        assertEquals($cmp(1,4), rcmp.compare(0));
        assertEquals($cmp(3,4), rcmp.compare(1));
        assertEquals($cmp(5,4), rcmp.compare(2));
        assertEquals($cmp(7,4), rcmp.compare(3));
    }
    
    @TestContract
    public void testLRCompare_cmp(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7});
        
        NativeComparator lcmp = target.lcmp($var.item(4).value(), CompareUtils.comparator().reversed());
        NativeComparator rcmp = target.rcmp($var.item(4).value(), CompareUtils.comparator().reversed());
        
        assertEquals($cmp(1,4), lcmp.compare(0));
        assertEquals($cmp(3,4), lcmp.compare(1));
        assertEquals($cmp(5,4), lcmp.compare(2));
        assertEquals($cmp(7,4), lcmp.compare(3));
        
        assertEquals($cmp(4,1), rcmp.compare(0));
        assertEquals($cmp(4,3), rcmp.compare(1));
        assertEquals($cmp(4,5), rcmp.compare(2));
        assertEquals($cmp(4,7), rcmp.compare(3));
    }
	
	@TestContract
    public void testEQCompare(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7});
        
        IntPredicate cmp = target.cmp($var.item(4).value());
        
        assertEquals($eq(1,4), cmp.test(0));
        assertEquals($eq(3,4), cmp.test(1));
        assertEquals($eq(5,4), cmp.test(2));
        assertEquals($eq(7,4), cmp.test(3));
        
    }
    
    @TestContract
    public void testComparator(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7,2});
        IntComparator icmp = target.comparator();
        
        assertEquals($cmp(1,3), icmp.compare(0,1));
        assertEquals($cmp(3,5), icmp.compare(1,2));
        assertEquals($cmp(1,7), icmp.compare(0,3));
        assertEquals($cmp(7,1), icmp.compare(3,0));
        assertEquals($cmp(5,5), icmp.compare(2,2));
        assertEquals($cmp(3,2), icmp.compare(1,4));
        
        NativeArray array = f.apply(new int[]{4,5,9,3,1});
        IntComparator acmp = target.comparator(array);
        
        assertEquals($cmp(1,5), acmp.compare(0,1));
        assertEquals($cmp(3,9), acmp.compare(1,2));
        assertEquals($cmp(1,3), acmp.compare(0,3));
        assertEquals($cmp(7,4), acmp.compare(3,0));
        assertEquals($cmp(5,9), acmp.compare(2,2));
        assertEquals($cmp(3,1), acmp.compare(1,4));
    }
    
    @TestContract
    public void testComparator_cmp(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7,2});
        IntComparator icmp = target.comparator(CompareUtils.comparator().reversed());
        
        assertEquals($cmp(3,1), icmp.compare(0,1));
        assertEquals($cmp(5,3), icmp.compare(1,2));
        assertEquals($cmp(7,1), icmp.compare(0,3));
        assertEquals($cmp(1,7), icmp.compare(3,0));
        assertEquals($cmp(5,5), icmp.compare(2,2));
        assertEquals($cmp(2,3), icmp.compare(1,4));
        
        NativeArray array = f.apply(new int[]{4,5,9,3,1});
        IntComparator acmp = target.comparator(array, CompareUtils.comparator().reversed());
        
        assertEquals($cmp(5,1), acmp.compare(0,1));
        assertEquals($cmp(9,3), acmp.compare(1,2));
        assertEquals($cmp(3,1), acmp.compare(0,3));
        assertEquals($cmp(4,7), acmp.compare(3,0));
        assertEquals($cmp(9,5), acmp.compare(2,2));
        assertEquals($cmp(1,3), acmp.compare(1,4));
    }
    
    @TestContract
    public void testToString(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5,7,2});
        String exp = $var.list(1,3,5,7,2).into(new ArrayList<>()).toString();
        assertEquals(exp, target.toString());
    }
    
    @TestContract
    public void testAllocate(Function<int[], NativeArray> f) {
        NativeArray target = f.apply(new int[]{1,3,5});
        NativeArray z = target.allocate(0);
        assertEquals(0, z.size());
        assertEquals(target.component(), z.component());
        
        NativeArray c = target.allocate(5);
        assertEquals(5, c.size());
        assertEquals(target.component(), c.component());
    }
	    
    private int $cmp(int a, int b) {
        return CompareUtils.cmp($var.item(a).value(), $var.item(b).value());
    }
	
	private boolean $eq(int a, int b) {
        return 0 == CompareUtils.cmp($var.item(a).value(), $var.item(b).value());
    }
    
}
