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

import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import javax.annotation.Resource;
import net.ranides.assira.collection.lookups.Lookup.LookupEntry;
import net.ranides.assira.collection.lookups.SortedLookup;
import net.ranides.assira.collection.suite.IteratorTester;
import net.ranides.assira.collection.suite.sets.SetTester;
import net.ranides.assira.test.TMap;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.junit.TestContract;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings("PMD.MethodNameRules")
public final class SortedLookupTester<K> {
    
    @Resource(name = "map!")
    private TMap<K,Integer> $var;
    
    @TestContract
    public void basicBounds_EntrySetF(SortedLookup<K> target) {
        SortedSet<LookupEntry<K>> set = target.fastEntrySet();
        assertThrows(NoSuchElementException.class, ()->
            set.first()
        );
        assertThrows(NoSuchElementException.class, ()->
            set.last()
        );
        $var.list(2,5,3,1,4).into(target);
        assertEquals( set.first(), $var.item(1).entry() );
        assertEquals( set.last(), $var.item(5).entry() );
    }
    
    @TestContract
    public void basicBounds_EntrySet(SortedLookup<K> target) {
        SortedSet<Entry<K, Integer>> set = target.entrySet();
        assertThrows(NoSuchElementException.class, ()->
            set.first()
        );
        assertThrows(NoSuchElementException.class, ()->
            set.last()
        );
        $var.list(2,5,3,1,4).into(target);
        assertEquals( set.first(), $var.item(1).entry() );
        assertEquals( set.last(), $var.item(5).entry() );
    }
    
    @TestContract
    public void basicKeys_Head(SortedLookup<K> target) {
        SetTester.assertHead($var.keys(), target.keySet(), $var.list().keys());
        $var.list(4, 7, 5, 3, 6).into(target);
        SetTester.assertHead($var.keys(), target.keySet(), $var.list(3, 4, 5, 6, 7).keys());
    }
    
    @TestContract
    public void basicKeys_Tail(SortedLookup<K> target) {
        SetTester.assertTail($var.keys(), target.keySet(), $var.list().keys());
        $var.list(4, 7, 5, 3, 6).into(target);
        SetTester.assertTail($var.keys(), target.keySet(), $var.list(3, 4, 5, 6, 7).keys());
    }
    
    @TestContract
    public void basicKeys_Sub(SortedLookup<K> target) {
        SetTester.assertTail($var.keys(), target.keySet(), $var.list().keys());
        $var.list(4, 7, 5, 3, 6).into(target);
        SetTester.assertTail($var.keys(), target.keySet(), $var.list(3, 4, 5, 6, 7).keys());
    }
    
    @TestContract
    public void basicEntries_Head(SortedLookup<K> target) {
        SetTester.assertHead($var.entries(), target.entrySet(), $var.list().entries());
        SetTester.assertHead($var.lookups(), target.fastEntrySet(), $var.list().lookups());
        $var.list(4, 7, 5, 3, 6).into(target);
        
        SetTester.assertHead($var.entries(), target.entrySet(), $var.list(3, 4, 5, 6, 7).entries());
        SetTester.assertHead($var.lookups(), target.fastEntrySet(), $var.list(3, 4, 5, 6, 7).lookups());
    }
    
    @TestContract
    public void basicEntries_Tail(SortedLookup<K> target) {
        SetTester.assertTail($var.entries(), target.entrySet(), $var.list().entries());
        SetTester.assertTail($var.lookups(), target.fastEntrySet(), $var.list().lookups());
        
        $var.list(4, 7, 5, 3, 6).into(target);
        
        SetTester.assertTail($var.entries(), target.entrySet(), $var.list(3, 4, 5, 6, 7).entries());
        SetTester.assertTail($var.lookups(), target.fastEntrySet(), $var.list(3, 4, 5, 6, 7).lookups());
    }
    
    @TestContract
    public void basicEntries_Sub(SortedLookup<K> target) {
        SetTester.assertSub($var.entries(), target.entrySet(), $var.list().entries());
        SetTester.assertSub($var.lookups(), target.fastEntrySet(), $var.list().lookups());
        
        $var.list(4, 7, 5, 3, 6).into(target);
        
        SetTester.assertSub($var.entries(), target.entrySet(), $var.list(3, 4, 5, 6, 7).entries());
        SetTester.assertSub($var.lookups(), target.fastEntrySet(), $var.list(3, 4, 5, 6, 7).lookups());
    }
    
    @SuppressWarnings("unchecked")
    @TestContract
    public void basicIterator(SortedLookup<K> target) {
        TMap.TItems<K,Integer> list1 = $var.list();
        TMap.TItems<K,Integer> list2 = $var.list(3, 4, 5, 6, 7);
        
        IteratorTester.orderIterator(target.keySet().iterator(), 0, list1.keys());
        IteratorTester.orderIterator(target.entrySet().iterator(), 0, list1.entries());
        IteratorTester.orderIterator(target.fastEntrySet().iterator(), 0, list1.lookups());
        IteratorTester.orderIterator((ListIterator)target.values().iterator(), 0, list1.values());
        
        $var.list(4, 7, 5, 3, 6).into(target);
        
        IteratorTester.orderIterator(target.keySet().iterator(), 0, list2.keys());
        IteratorTester.orderIterator(target.entrySet().iterator(), 0, list2.entries());
        IteratorTester.orderIterator(target.fastEntrySet().iterator(), 0, list2.lookups());
        IteratorTester.orderIterator((ListIterator)target.values().iterator(), 0, list2.values());
    }
    
    @SuppressWarnings("unchecked")
    @TestContract
    public void basicIteratorFrom(SortedLookup<K> target) {
        TMap.TItems<K,Integer> list1 = $var.list();
        TMap.TItems<K,Integer> list2 = $var.list(3, 4, 5, 6, 7);
        TMap.TItem<K,Integer> item = $var.item(4);
        
        IteratorTester.orderIterator(target.keySet().iterator(item.key()), 0, list1.keys());
        IteratorTester.orderIterator(target.entrySet().iterator(item.entry()), 0, list1.entries());
        IteratorTester.orderIterator(target.fastEntrySet().iterator(item.lookup()), 0, list1.lookups());
        
        $var.list(4, 7, 5, 3, 6).into(target);
        
        IteratorTester.orderIterator(target.keySet().iterator(item.key()), 2, list2.keys());
        IteratorTester.orderIterator(target.entrySet().iterator(item.entry()), 2, list2.entries());
        IteratorTester.orderIterator(target.fastEntrySet().iterator(item.lookup()), 2, list2.lookups());
    }
    
    @SuppressWarnings("unchecked")
    @TestContract
    public void basicSubmapIteratorFrom(SortedLookup<K> target) {
        TMap.TItems<K,Integer> list = $var.list(3, 4, 5, 6, 7);
        TMap.TItem<K,Integer> item = $var.item(4);

        $var.list(4, 7, 5, 3, 6, 1, 8).into(target);
        SortedLookup<K> map = target.subMap($var.item(1).key(), $var.item(8).key());
        
        IteratorTester.subIterator(map.keySet().iterator(item.key()), 2, list.keys());
        IteratorTester.subIterator(map.entrySet().iterator(item.entry()), 2, list.entries());
        IteratorTester.subIterator(map.fastEntrySet().iterator(item.lookup()), 2, list.lookups());
    }
    
    
    @TestContract
    public void fastEntrySet_Contains_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        assertEquals(0, set.size());
		
        $var.list().item(1).item(1,1).item(2).item(3).into(target);
		assertEquals(4, set.size());
        assertEquals(true, set.contains($var.item(1).lookup()));
        assertEquals(true, set.contains($var.item(1,1).lookup()));
        assertEquals(true, set.contains($var.item(2).lookup()));
        assertEquals(true, set.contains($var.item(3).lookup()));
        
        assertEquals(false, set.contains($var.item(4).lookup()));
        assertEquals(false, set.contains(new Object()));
        
	}
    
    @TestContract
    public void fastEntrySet_Contains(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        assertEquals(0, set.size());
		
        $var.list(1,2,3,5).into(target);
		assertEquals(4, set.size());
        assertEquals(true, set.contains($var.item(1).lookup()));
        assertEquals(true, set.contains($var.item(2).lookup()));
        assertEquals(true, set.contains($var.item(3).lookup()));
        assertEquals(true, set.contains($var.item(5).lookup()));
        
        assertEquals(false, set.contains($var.item(4).lookup()));
        assertEquals(false, set.contains(new Object()));
        
	}
    
    @TestContract
    public void fastEntrySet_Remove(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        $var.list(1,2,3,7).into(target);
		
        assertEquals(false, set.remove($var.item(1).lookup(5)));
        assertEquals(true, set.remove($var.item(1).lookup()));
        assertEquals(false, set.remove($var.item(4).lookup()));
        assertEquals(false, set.remove(new Object()));
        
        assertEquals(false, set.contains($var.item(1).lookup()));
        assertEquals(true, set.contains($var.item(2).lookup()));
        assertEquals(true, set.contains($var.item(3).lookup()));
        assertEquals(true, set.contains($var.item(7).lookup()));
        
	}
    
    @TestContract
    public void fastEntrySet_Remove_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        $var.list().item(1).item(1,1).item(2).item(3).into(target);
		
        assertEquals(false, set.remove($var.item(1).lookup(5)));
        assertEquals(true, set.remove($var.item(1).lookup()));
        assertEquals(false, set.remove($var.item(4).lookup()));
        assertEquals(false, set.remove(new Object()));
        
        assertEquals(false, set.contains($var.item(1).lookup()));
        assertEquals(true, set.contains($var.item(1,1).lookup()));
        assertEquals(true, set.contains($var.item(2).lookup()));
        assertEquals(true, set.contains($var.item(3).lookup()));
        
	}
    
    @TestContract
    public void fastEntrySet_Iterator_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        assertEquals(0, set.size());
		$var.list().item(1).item(1,1).item(2).item(3).into(target);
		assertEquals(4, set.size());

        Iterator<LookupEntry<K>> iterator = set.iterator();
        while(iterator.hasNext()) {
            LookupEntry<K> entry = iterator.next();
            assertEquals(target.getInt(entry.getKey()), entry.getIntValue());
        }
        
        assertThrows(NoSuchElementException.class, ()-> iterator.next());
	}
    
    @TestContract
    public void fastEntrySet_Iterator(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
		
        assertEquals(0, set.size());
		$var.list(1,2,3,5).into(target);
		assertEquals(4, set.size());

        Iterator<LookupEntry<K>> iterator = set.iterator();
        while(iterator.hasNext()) {
            LookupEntry<K> entry = iterator.next();
            assertEquals(target.getInt(entry.getKey()), entry.getIntValue());
        }
        
        assertThrows(NoSuchElementException.class, ()-> iterator.next());
	}
    
    @TestContract
    public void fastEntrySet_Iterator_SetValue_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
                
		assertEquals(0, set.size());
		$var.list().item(1).item(1,1).item(2).item(3).into(target);
        K k1 = $var.item(1).key();
		K k2 = $var.item(2).key();
        int v1 = $var.item(101).valueInt();
        int v2 = $var.item(102).valueInt();
		
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			LookupEntry<K> entry = iterator.next();
			if(k1.equals(entry.getKey())) {
                entry.setValue(v1);
            }
            if(k2.equals(entry.getKey())) {
                entry.setValue(v2);
            }
		}
		assertContains(target, $var.item(1).key(), v1);
		assertContains(target, $var.item(2).key(), v2);
		assertContains(target, $var.item(1,1));
		assertContains(target, $var.item(3));
	}
    
    @TestContract
    public void fastEntrySet_Iterator_SetValue(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
                
		assertEquals(0, set.size());
		$var.list(1,2,3,5).into(target);
        K k1 = $var.item(1).key();
		K k2 = $var.item(2).key();
        int v1 = $var.item(101).value();
        int v2 = $var.item(102).value();
		
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			LookupEntry<K> entry = iterator.next();
			if(k1.equals(entry.getKey())) {
                entry.setValue(v1);
            }
            if(k2.equals(entry.getKey())) {
                entry.setValue(v2);
            }
		}
		assertContains(target, $var.item(1).key(), v1);
		assertContains(target, $var.item(2).key(), v2);
		assertContains(target, $var.item(5));
		assertContains(target, $var.item(3));
	}
    
    @TestContract
    public void fastEntrySet_Iterator_Remove_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
        
		assertEquals(0, set.size());
		$var.list().item(1).item(1,1).item(2).item(3).into(target);
        K k1 = $var.item(1).key();
		K k2 = $var.item(2).key();
		
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			LookupEntry<K> entry = iterator.next();
            K key = entry.getKey();
            
			if(k1.equals(key)) {
                iterator.remove();
            }
            if(k2.equals(key)) {
                iterator.remove();
                assertThrows(IllegalStateException.class, ()-> iterator.remove());
            }
		}
        
        assertEquals(2, target.size());
        assertNotContains(target, $var.item(1));
        assertNotContains(target, $var.item(2));
		assertContains(target, $var.item(1,1));
		assertContains(target, $var.item(3));
        
        set.clear();
        assertEquals(0, target.size());
	}
    
    @TestContract
    public void fastEntrySet_Iterator_Remove(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
        
		assertEquals(0, set.size());
		$var.list(1,2,3,5).into(target);
        K k1 = $var.item(1).key();
		K k2 = $var.item(2).key();
		
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			LookupEntry<K> entry = iterator.next();
            K key = entry.getKey();
            
			if(k1.equals(key)) {
                iterator.remove();
            }
            if(k2.equals(key)) {
                iterator.remove();
                assertThrows(IllegalStateException.class, ()-> iterator.remove());
            }
		}
        
        assertEquals(2, target.size());
        assertNotContains(target, $var.item(1));
        assertNotContains(target, $var.item(2));
		assertContains(target, $var.item(3));
		assertContains(target, $var.item(5));
        
        set.clear();
        assertEquals(0, target.size());
	}
    
    @TestContract
    public void fastEntrySet_Iterator_RemoveAll_HC(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
        
		assertEquals(0, set.size());
        $var.urange(1024).into(target);
            
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			iterator.next();
            iterator.remove();
		}
        assertEquals(0, target.size());
	}
    
    @TestContract
    public void fastEntrySet_Iterator_RemoveAll(SortedLookup<K> target) {
        Set<LookupEntry<K>> set = target.fastEntrySet();
        
		assertEquals(0, set.size());
        $var.range(1024).into(target);
            
		Iterator<LookupEntry<K>> iterator = set.iterator();
		while(iterator.hasNext()) {
			iterator.next();
            iterator.remove();
		}
        assertEquals(0, target.size());
	}
    
    private void assertContains(SortedLookup<K> target, K key, int value) {
        assertEquals(true, target.containsKey(key));
        assertEquals(value, target.getInt(key));
    }
    
    private void assertContains(SortedLookup<K> target, TMap.TItem<K,?> item) {
		assertContains(target, item.key(), item.valueInt());
    }
    
    private void assertNotContains(SortedLookup<K> target, TMap.TItem<K,?> item) {
        assertEquals(false, target.containsKey(item.key()));
        assertEquals(null, target.get(item.key()));
    }

}
