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

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.IntUnaryOperator;
import net.ranides.assira.ContractTesters;
import net.ranides.assira.collection.arrays.IntArrays;
import net.ranides.assira.collection.mockup.TMaps;
import net.ranides.assira.collection.mockup.TPoint;
import net.ranides.assira.functional.IntCoFunction;
import net.ranides.assira.functional.IntProjectionFunction;
import org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.reflection.util.MethodUtils;
import net.ranides.assira.test.TCollection;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class IntListUtilsTest {
	
	@Test
    public void testFirst() {
		IntList list8 = IntArrays.asList(1, 12, 13, 4, 15, 6, 17, 8);
		IntList list1 = IntArrays.asList(1);
		IntList list0 = IntArrays.asList();
		
        assertEquals(1, IntListUtils.first(list8));
        assertEquals(1, IntListUtils.first(list1));
		
        assertThrows(NoSuchElementException.class, ()-> {
			IntListUtils.first(list0);
		});
		assertThrows(NoSuchElementException.class, ()->{
			IntListUtils.first(list8, v -> false);
		});
		assertThrows(NoSuchElementException.class, ()->{
			IntListUtils.first(list8, 1, 3, v -> false);
		});
		
		assertEquals(12, IntListUtils.first(list8, 1, 3, v -> true));
		
		assertEquals(12, IntListUtils.first(list8, v -> v>10));
		assertEquals(12, IntListUtils.first(list8, 1, 3, v -> v>10));
		assertEquals(15, IntListUtils.first(list8, 3, 5, v -> v>10));
    }
	
	@Test
    public void testLast() {
		IntList list8 = IntArrays.asList(1, 12, 13, 4, 15, 6, 17, 8);
		IntList list1 = IntArrays.asList(1);
		IntList list0 = IntArrays.asList();
		
        assertEquals(8, IntListUtils.last(list8));
        assertEquals(1, IntListUtils.last(list1));
		
        assertThrows(NoSuchElementException.class, ()-> {
			IntListUtils.last(list0);
		});
		assertThrows(NoSuchElementException.class, ()-> {
			IntListUtils.last(list8, v -> false);
		});
		assertThrows(NoSuchElementException.class, ()-> {
			IntListUtils.last(list8, 1, 3, v -> false);
		});
		
		assertEquals(4, IntListUtils.last(list8, 1, 4, v -> true));
		
		assertEquals(17, IntListUtils.last(list8, v -> v>10));
		assertEquals(15, IntListUtils.last(list8, 1, 6, v -> v>10));
		assertEquals(13, IntListUtils.last(list8, 1, 3, v -> v>10));
    }
	
	@Test
	public void testFilter() {
		assertEquals(IntArrays.asList(2,4,6), IntListUtils.filter(IntArrays.asList(1,2,3,4,5,6), v -> 0==v%2));
	}
	
	@Test
	public void testMap() {
		
		TCollection<TPoint> $var = TMaps.MAP_PI.keys();
		
		IntFunction<TPoint> fget = (IntFunction<TPoint> & Serializable)
			v -> $var.item(v).value();
		
		Function<int[], List<TPoint>> fgen = (Function<int[], List<TPoint>> & Serializable)
			array -> IntListUtils.map(new IntArrayList(array), fget);
		
		ContractTesters.runner()
			.ignore("CollectionTester.basicAdd")
			.ignore("CollectionTester.basicAddContains")
			
			.ignore("ListTester.basicAdd")
			.ignore("ListTester.basicAddAll")
			.ignore("ListTester.basicAddAllAt")
			.ignore("ListTester.basicAddAt")
			.ignore("ListTester.basicClearAdd")
			.ignore("ListTester.basicIterator_Add")
			.ignore("ListTester.basicIterator_Set")
			.ignore("ListTester.basicSet")
			.ignore("ListTester.basicSublist_add")
			.ignore("ListTester.basicSublist_addAll")
			.ignore("ListTester.basicSublist_clearAdd")
			.ignore("ListTester.basicSublist_set")
			
			.param("collection!", $var)
			.function(new int[0], fgen)
			.run();
		assertTrue(true);
	}
	
	@Test
	public void testMapIntOperator() {
		
		TCollection<Integer> $var = TMaps.MAP_PI.values();
		
		IntUnaryOperator fget = (IntUnaryOperator & Serializable)
			v -> $var.item(v).value();
		
		Function<int[], IntList> fgen = (Function<int[], IntList> & Serializable)
			array -> IntListUtils.map(new IntArrayList(array), fget);
		
		ContractTesters.runner()
			.ignore("CollectionTester.basicAdd")
			.ignore("CollectionTester.basicAddContains")
			
			.ignore("IntCollectionTester.basicAdd")
			.ignore("IntCollectionTester.basicAddContains")
			
			.ignore("ListTester.basicAdd")
			.ignore("ListTester.basicAddAll")
			.ignore("ListTester.basicAddAllAt")
			.ignore("ListTester.basicAddAt")
			.ignore("ListTester.basicClearAdd")
			.ignore("ListTester.basicIterator_Add")
			.ignore("ListTester.basicIterator_Set")
			.ignore("ListTester.basicSet")
			.ignore("ListTester.basicSublist_add")
			.ignore("ListTester.basicSublist_addAll")
			.ignore("ListTester.basicSublist_clearAdd")
			.ignore("ListTester.basicSublist_set")

			.ignore("IntListTester.basicAddAllAt_Collection")
			.ignore("IntListTester.basicAddAllAt_List")
			.ignore("IntListTester.basicAddAll_Collection")
			.ignore("IntListTester.basicAddAll_List")
			.ignore("IntListTester.basicAddElements")
			.ignore("IntListTester.basicAddElementsEnd")
			.ignore("IntListTester.basicStackPeek")
			.ignore("IntListTester.basicStackPush")
			
			.param("collection!", $var)
			.function(new int[0], fgen)
			.run();
		assertTrue(true);
	}
	
	@Test
	public void testMapProjection() {
		
		TCollection<Integer> $var = TMaps.MAP_PI.values();
		
		IntUnaryOperator fget = (IntUnaryOperator & Serializable)
			v -> $var.item(v).value();
		
		IntCoFunction fset = (IntCoFunction & Serializable)
			v -> v / 10;
			
		Function<int[], IntList> fgen = (Function<int[], IntList> & Serializable)
			array -> IntListUtils.map(new IntArrayList(array), IntProjectionFunction.produce(fget, fset));
		
		ContractTesters.runner()
			.param("collection!", $var)
			.function(new int[0], fgen)
			.run();
		assertTrue(true);
	}
	
	@Test
	public void testProduce() {
		TCollection<Integer> $var = TMaps.MAP_PI.values();
		
		Function<int[], IntList> fgen = (Function<int[], IntList> & Serializable)
			array -> IntListUtils.produce(
				array.length, 
				(IntUnaryOperator & Serializable) i -> $var.item(array[i]).value()
			);
		
		ContractTesters.runner()
			.ignore("CollectionTester.basicAdd")
			.ignore("CollectionTester.basicAddContains")
			.ignore("CollectionTester.basicRemove")
			.ignore("CollectionTester.basicRetainAll")
			
			.ignore("IntCollectionTester.basicAdd")
			.ignore("IntCollectionTester.basicAddContains")
			.ignore("IntCollectionTester.basicRemove")
			.ignore("IntCollectionTester.basicRetainAll")
			
			.ignore("ListTester.basicAdd")
			.ignore("ListTester.basicAddAll")
			.ignore("ListTester.basicAddAllAt")
			.ignore("ListTester.basicAddAt")
			.ignore("ListTester.basicRemove")
			.ignore("ListTester.basicRemoveRetainAll")
			.ignore("ListTester.basicRemoveIndex")
			.ignore("ListTester.basicClear")
			.ignore("ListTester.basicClearAdd")
			.ignore("ListTester.basicIterator_Add")
			.ignore("ListTester.basicIterator_Set")
			.ignore("ListTester.basicIterator_Remove")
			.ignore("ListTester.basicSet")
			.ignore("ListTester.basicSublist_add")
			.ignore("ListTester.basicSublist_addAll")
			.ignore("ListTester.basicSublist_clear")
			.ignore("ListTester.basicSublist_clearAdd")
			.ignore("ListTester.basicSublist_set")
			
			.ignore("IntListTester.basicAddAllAt_Collection")
			.ignore("IntListTester.basicAddAllAt_List")
			.ignore("IntListTester.basicAddAll_Collection")
			.ignore("IntListTester.basicAddAll_List")
			.ignore("IntListTester.basicAddElements")
			.ignore("IntListTester.basicAddElementsEnd")
			.ignore("IntListTester.basicRemoveRetainAll")
			.ignore("IntListTester.basicRemoveElements")
			.ignore("IntListTester.basicStackPeek")
			.ignore("IntListTester.basicStackPop")
			.ignore("IntListTester.basicStackPush")
			
			.param("collection!", $var)
			.function(new int[0], fgen)
			.run();
		assertTrue(true);
	}
	
	@Test
	public void testSublist() {
		TCollection<Integer> $var = TMaps.MAP_IS.keys();

		ContractTesters.runner()
			.param("collection!", $var)
			.function(new int[0], array -> getList($var.list(array).valuesInt()))
			.run();
		assertTrue(true);
	}
	
	private static IntList getList(int[] values) {
		IntList list = new IntArrayList(values);
        return (IntList)Proxy.newProxyInstance(IntList.class.getClassLoader(), new Class[]{IntList.class, Serializable.class}, (InvocationHandler & Serializable)(Object proxy, Method method, Object[] args)->{
			if("subList".equals(method.getName())) {
				return IntListUtils.sublist((IntList)proxy, (Integer)args[0], (Integer)args[1]);
			}
            return MethodUtils.invoke(method, list, args);
        });
    }

}
