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

import java.util.NoSuchElementException;
import java.util.function.Function;
import javax.annotation.Resource;
import net.ranides.assira.collection.IntCollection;
import net.ranides.assira.collection.lists.IntArrayList;
import net.ranides.assira.collection.lists.IntList;
import net.ranides.assira.collection.sets.IntAVLTreeSet;
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.MethodNameRules")
public class IntListTester {
    
    private static final int[] VA1 = new int[]{1,3,5,7,9};
    private static final int[] VA2 = new int[]{1,11,12,13,3,5,7,9};
    private static final int[] VA3 = new int[]{21,22,1,11,12,13,3,5,7,9};
    private static final int[] VA4 = new int[]{21,22,1,11,12,13,3,5,7,31,32,33,9};
    private static final int[] VA5 = new int[]{21,22,1,11,12,13,3,5,7,31,32,33,9,41,42,43};
    private static final int[] VA6 = new int[]{21,22,1,11,12,13,3,5,7,31,32,33,9,41,42,43,51,52,53};
    
    private static final int[] V1 = new int[]{1,3,5,7};
    private static final int[] V2 = new int[]{1,3,5,7,9};
    private static final int[] V3 = new int[]{1,3,5,7,9,11};
    
    @Resource(name = "collection!")
    private TCollection<?> $var;
    
    @TestContract
    public void basicGetElements(Function<int[], IntList> f) {
		IntList target = f.apply(V3);
        
        int[] buffer = new int[5];
        target.getElements(2, buffer, 1, 3);
        assertArrayEquals($var.list(0,5,7,9,0).valuesInt(), buffer);
    }
	
	@TestContract
	public void basicRemoveRetainAll(Function<int[], IntList> f) {
		
		IntList target2 = f.apply(new int[]{1,3,5,7,9,11});
		target2.removeAll(f.apply(new int[]{1,5,7,11}));
		
		assertEquals( $list(3,9), target2);
		
		IntList target3 = f.apply(new int[]{1,3,5,7,9,11});
		target3.retainAll(f.apply(new int[]{2,5,9,12}));
		assertEquals( $list(5,9), target3);
	}
	
    
    @TestContract
    public void basicRemoveElements(Function<int[], IntList> f) {
        IntList target = f.apply(V3);
        
        target.removeElements(0, 0);
        target.removeElements(1, 1);
        
        assertEquals($list(1,3,5,7,9,11), target);
        
        // IllegalArgumentException | IndexOutOfBoundsException | ArrayIndexOutOfBoundsException
        assertThrows(Exception.class, ()->{
            target.removeElements(0, -1);
        });
        // IllegalArgumentException | IndexOutOfBoundsException | ArrayIndexOutOfBoundsException
        assertThrows(Exception.class, ()->{
            target.removeElements(3, 2);
        });
        // IllegalArgumentException | IndexOutOfBoundsException | ArrayIndexOutOfBoundsException
        assertThrows(Exception.class, ()->{
            target.removeElements(-1, 1);
        });
        // IllegalArgumentException | IndexOutOfBoundsException | ArrayIndexOutOfBoundsException
        assertThrows(Exception.class, ()->{
            target.removeElements(2, 8);
        });
        
        target.removeElements(1, 2);
        assertEquals($list(1,5,7,9,11), target);
        
        target.removeElements(2, 4);
        assertEquals($list(1,5,11), target);
    }
    
    @TestContract
    public void basicAddElements(Function<int[], IntList> f) {
        IntList target = f.apply(V2);

        target.addElements(2, $var.list(31,32,33,34,35,36,37,38,39).valuesInt(), 3, 4);
        assertEquals($list(1,3,34,35,36,37,5,7,9), target);
        
        target.addElements(7, $var.list(41,42,43).valuesInt());
        assertEquals($list(1,3,34,35,36,37,5,41,42,43,7,9), target);
        
        target.addElements(target.size(), $var.list(51,52).valuesInt());
        assertEquals($list(1,3,34,35,36,37,5,41,42,43,7,9,51,52), target);
    }
    
    @TestContract
    public void basicAddElementsEnd(Function<int[], IntList> f) {
        IntList target = f.apply(V2);

        
        target.addElements(target.size(), $var.list(51,52).valuesInt());
        assertEquals($list(1,3,5,7,9, 51,52), target);
        
        target.addElements(target.size(), $var.list(71,72,73).valuesInt());
        assertEquals($list(1,3,5,7,9, 51,52, 71,72,73), target);
    }
    
    @TestContract
    public void basicAddAll_Collection(Function<int[], IntList> f) {
        IntList target = f.apply(VA1);
        
        target.addAll($set(51,52,53));
        assertEquals($list(1,3,5,7,9, 51,52,53), target);
        
        target.addAll($set(7,8,9));
        assertEquals($list(1,3,5,7,9, 51,52,53, 7,8,9), target);
    }
    
    @TestContract
    public void basicAddAllAt_Collection(Function<int[], IntList> f) {
        IntList target = f.apply(VA1);
        
        target.addAll(1, $set(11,12,13));
        assertEquals($list(VA2), target);
        
        target.addAll(0,$set(21,22));
        assertEquals($list(VA3), target);
        
        target.addAll(target.size()-1, $set(31,32,33));
        assertEquals($list(VA4), target);
        
        target.addAll(target.size(),$set(41,42,43));
        assertEquals($list(VA5), target);
        
        target.addAll(target.size(),$set());
        assertEquals($list(VA5), target);
        
        target.addAll($set(51,52,53));
        assertEquals($list(VA6), target);
    }
    
    @TestContract
    public void basicAddAll_List(Function<int[], IntList> f) {
        IntList target = f.apply(VA1);
        
       
        target.addAll($list(51,52,53));
        assertEquals($list(1,3,5,7,9, 51,52,53), target);
        
        target.addAll($list(7,8,9));
        assertEquals($list(1,3,5,7,9, 51,52,53, 7,8,9), target);
    }
        
    @TestContract
    public void basicAddAllAt_List(Function<int[], IntList> f) {
        IntList target = f.apply(VA1);
        
        target.addAll(1, $list(11,12,13));
        assertEquals($list(VA2), target);
        
        target.addAll(0,$list(21,22));
        assertEquals($list(VA3), target);
        
        target.addAll(target.size()-1, $list(31,32,33));
        assertEquals($list(VA4), target);
        
        target.addAll(target.size(),$list(41,42,43));
        assertEquals($list(VA5), target);
        
        target.addAll(target.size(),$list());
        assertEquals($list(VA5), target);
        
        target.addAll($list(51,52,53));
        assertEquals($list(VA6), target);
    }
    
    @TestContract
    public void basicToIntArray(Function<int[], IntList> f) {
        IntList target = f.apply(V2);
        
        assertArrayEquals($var.list(1,3,5,7,9).valuesInt(), target.toIntArray());
//        assertArrayEquals($var.list(1,3,5,7,9).valuesInt(), target.toIntArray(null));
        assertArrayEquals($var.list(1,3,5,7,9).valuesInt(), target.toIntArray(new int[0]));
        assertArrayEquals($var.list(1,3,5,7,9).valuesInt(), target.toIntArray(new int[1]));
        assertArrayEquals($var.list(1,3,5,7,9,0,0).valuesInt(), target.toIntArray(new int[7]));
    }
	
	@TestContract
    public void basicStackPop(Function<int[], IntList> f) {
        IntList target = f.apply(V2);
        
		assertEquals($var.item(9).valueInt(), target.pop());
		assertEquals($var.item(7).valueInt(), target.pop());
		assertEquals($var.item(5).valueInt(), target.pop());
		assertEquals($var.item(3).valueInt(), target.pop());
		assertEquals($var.item(1).valueInt(), target.pop());
		
		assertThrows(NoSuchElementException.class, ()->{ 
			target.pop();
		});
    }
	
	@TestContract
    public void basicStackPush(IntList target) {
        target.push($var.item(1).valueInt());
        target.push($var.item(2).valueInt());
        target.push($var.item(3).valueInt());
		
		assertEquals($var.item(3).valueInt(), target.pop());
		assertEquals($var.item(2).valueInt(), target.pop());
		assertEquals($var.item(1).valueInt(), target.pop());
        
		assertThrows(NoSuchElementException.class, ()->{ 
			target.pop();
		});
    }
	
	@TestContract
    public void basicStackPeek(Function<int[], IntList> f) {
        IntList target = f.apply(V1);
        
		target.push($var.item(9).valueInt());
		
		assertEquals($var.item(9).valueInt(), target.top());
		assertEquals($var.item(9).valueInt(), target.pop());
		
		assertEquals($var.item(7).valueInt(), target.top());
		assertEquals($var.item(7).valueInt(), target.pop());
		
		assertEquals($var.item(5).valueInt(), target.peek(0));
		assertEquals($var.item(3).valueInt(), target.peek(1));
		assertEquals($var.item(1).valueInt(), target.peek(2));
		
		assertThrows(NoSuchElementException.class, ()->{
			target.peek(3);
		});
		
		target.clear();
		assertThrows(NoSuchElementException.class, ()->{
			target.top();
		});
		assertThrows(NoSuchElementException.class, ()->{
			target.peek(0);
		});
    }
    
    private IntCollection $set(int... values) {
        return $var.list(values).intoInt(new IntAVLTreeSet());
    }
    
    private IntList $list(int... values) {
        return $var.list(values).intoInt(new IntArrayList());
    }

}
