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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Resource;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.test.TCollection;
import net.ranides.assira.junit.TestContract;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class CollectionTester<T> {
    
    @Resource(name = "collection!")
    private TCollection<T> $var;
    
    @TestContract
    public void basicAdd(Collection<T> target) {
		target.add($var.item(0).value());
        assertEquals(1, target.size());
		
        target.add($var.item(1).value());
        assertEquals(2, target.size());
        
		target.add($var.item(2).value());
        assertEquals(3, target.size());
    }
    
    @TestContract
    public void basicAddContains(Collection<T> target) {
		target.add($var.item(0).value());
        assertEquals(1, target.size());
		
        target.add($var.item(1).value());
        assertEquals(2, target.size());
        
		target.add($var.item(2).value());
        assertEquals(3, target.size());
        
        assertTrue( target.contains($var.item(0).value()));
        assertTrue( target.contains($var.item(1).value()));
        assertTrue( target.contains($var.item(2).value()));
        assertFalse( target.contains($var.item(3).value()));
    }
    
    @TestContract
    public void basicContains(Function<int[], Collection<T>> f) {
        Collection<T> target = f.apply(new int[]{1,2,3,4,5});
		
        assertTrue( target.contains($var.item(1).value()));
        assertFalse( target.contains($var.item(6).value()));
        
        assertTrue( target.containsAll($list(1,2,3)) );
        assertTrue( target.containsAll($list(3,4,5)) );
        assertTrue( target.containsAll($list(1,2,3,4,5)) );
        
        assertFalse( target.containsAll($list(6)) );
        assertFalse( target.containsAll($list(1,2,6)) );
        assertFalse( target.containsAll($list(1,6,2,3)) );
    }
    
    @TestContract
    public void basicRemove(Function<int[], Collection<T>> f) {
        Collection<T> target = f.apply(new int[]{1,2,3,4,5});
        assertContentEquals($list(1,2,3,4,5), target);

        assertTrue(target.remove($var.item(2).value()));
        assertContentEquals($list(1,3,4,5), target);
        
        assertTrue(target.remove($var.item(4).value()));
        assertContentEquals($list(1,3,5), target);
        
        assertFalse(target.remove($var.item(7).value()));
        assertContentEquals($list(1,3,5), target);
        
        assertFalse(target.removeAll($list(10,11)));
        assertContentEquals($list(1,3,5), target);
        
        assertTrue(target.removeAll($list(1,3,5,9)));
        assertEmpty(target);
    }
    
    @TestContract
    public void basicRetainAll(Function<int[], Collection<T>> f) {
        Collection<T> target = f.apply(new int[]{1,2,3,4,5});
        assertContentEquals($list(1,2,3,4,5), target);

        assertFalse(target.retainAll($list(1,2,3,4,5,9)));
        assertContentEquals($list(1,2,3,4,5), target);
        
        assertTrue(target.retainAll($list(1,3,10)));
        assertContentEquals($list(1,3), target);

        assertTrue(target.retainAll($list(12)));
        assertEmpty(target);
    }
    
    @TestContract
    public void basicToArray(Function<int[], Collection<T>> f) {
        Collection<T> target = f.apply(new int[]{1,2,3,4,5});
        Object[] t = target.toArray();
        Object[] e = $list(1,2,3,4,5).toArray();
        assertArrayContentEquals(e, t);
    }
    
    @TestContract
    public void basicToArrayG(Function<int[], Collection<T>> f) {
        // strasznie tu oszukujemy, licząc na type erasure
        Collection<T> target = f.apply(new int[]{1,2,3,4,5});
        Object[] e = $list(1,2,3,4,5).toArray();
        
        assertArrayContentEquals(e, target.toArray((T[])new Object[0]));
        assertArrayContentEquals(e, target.toArray((T[])new Object[3]));
        
        List<T> list = $list(1,2,3,4,5);
        list.add(null);
        list.add(null);
        list.add(null);
        Object[] e8 = list.toArray();
        assertArrayContentEquals(e8, target.toArray((T[])new Object[8]));
    }
    
    private List<T> $list(int... values) {
        return $var.list(values).into(new ArrayList<T>());
    }
    
    private static <Q> void assertContentEquals(Collection<Q> a, Collection<Q> b) {
//		System.out.printf("a = %s : %s%n", a.getClass(), a);
//		System.out.printf("b = %s : %s%n", b.getClass(), b);
        assertTrue("assertContentEquals: a IN b", a.containsAll(b));
        assertTrue("assertContentEquals: b IN a", b.containsAll(a));
    }
    
    private static void assertArrayContentEquals(Object[] a, Object[] b) {
        List<Object> alist = Arrays.asList(a);
        List<Object> blist = Arrays.asList(b);
        assertTrue("assertContentEquals", alist.containsAll(blist) && blist.containsAll(alist));
    }
    
    private void assertEmpty(Collection<T> target) {
        assertEquals(0, target.size());
        assertTrue(target.isEmpty());
        assertContentEquals($list(), target);
    }

}
