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

import java.util.function.Function;
import javax.annotation.Resource;
import net.ranides.assira.ContractTesters;
import net.ranides.assira.collection.lists.IntRange;
import net.ranides.assira.collection.suite.IteratorTester.IteratorParams;
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 RMQTester<K> {
    
    @Resource(name = "collection!")
    private TCollection<K> $var;

    public static final TCollection<String> NUMBERS = new TCollection<String>() {
        @Override
        protected String value(int index, int n) {
            if(n != 0) {
                throw new UnsupportedOperationException("NO TEST");
            }
            return String.valueOf(index);
        }

        @Override
        protected int cmp(String a, String b) {
            return Integer.compare(Integer.parseInt(a), Integer.parseInt(b));
        }

        @Override
        protected int hash(String value) {
            return Integer.parseInt(value);
        }
    };
    
    private static final int[] V1 = new int[]{0,1,2,3,4,5,6,7,8,9};
    private static final int[] V2 = new int[]{9,8,7,6,5,4,3,2,1,0};
    
    @TestContract
    public void testFind(Function<int[], RMQList<K>> f) {
        RMQList list1 = f.apply(V1);
        
        assertEquals($var.item(9).value(), list1.find());
        assertEquals($var.item(3).value(), list1.find(0,4));
        assertEquals($var.item(6).value(), list1.find(4,7));
        assertEquals($var.item(9).value(), list1.find(4,10));
        assertEquals($var.item(9).value(), list1.find(8,10));
        assertEquals($var.item(9).value(), list1.find(9,10));
        assertEquals($var.item(8).value(), list1.find(8,9));
        
        RMQList list2 = f.apply(V2);
        assertEquals($var.item(9).value(), list2.find());
        assertEquals($var.item(9).value(), list2.find(0,4));
        assertEquals($var.item(5).value(), list2.find(4,7));
        assertEquals($var.item(5).value(), list2.find(4,10));
        assertEquals($var.item(1).value(), list2.find(8,10));
        assertEquals($var.item(0).value(), list2.find(9,10));
        assertEquals($var.item(1).value(), list2.find(8,9));
    }
    
    @TestContract
    public void testFindBounds(Function<int[], RMQList<K>> f) {
        RMQList target = f.apply(V1);
        
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(-1,4);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(30,35);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(10,11);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(11,12);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(0,15);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(0,11);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(5,11);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(6,5);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(5,5);
        });
        assertThrows(IndexOutOfBoundsException.class, ()->{
            target.find(12,12);
        });
    }
    
    @TestContract
    public void testFindAppend(RMQList<K> target) {
        target.add($var.item(1).value());
        assertEquals($var.item(1).value(), target.find());
        
        target.add($var.item(2).value());
        target.add($var.item(3).value());
        assertEquals($var.item(3).value(), target.find());
        
        $var.list(4,5,6).into(target);
        assertEquals($var.item(6).value(), target.find());
    }
    
    @TestContract
    public void testIterator(Function<int[], RMQList<K>> f) {
        ContractTesters
            .runner()
            .param("iterator.array!", $var.asRangeGenerator() )
            .function(new IteratorParams(), p -> f.apply($range(p.size)).listIterator(p.index))
            .run();
    }

    private static int[] $range(int size) {
        return new IntRange(0, size).toIntArray();
    }
    
}
