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

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.annotation.Resource;
import net.ranides.assira.collection.maps.MultiMap;
import net.ranides.assira.functional.Fold;
import net.ranides.assira.generic.CompareUtils;
import net.ranides.assira.test.TMap;
import net.ranides.assira.test.TMap.TItems;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.junit.TestContract;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings("element-type-mismatch")
public final class MultiMapTester<K,V> {
    
    @Resource(name = "map!")
    private TMap<K,V> $var;
        
    @TestContract
    public void basicGetAll(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();
        K K4 = $var.item(4).key();
        
        init(target);
        
        assertContentEquals($var.list(101,102,103), target.getAll(K1));
        assertContentEquals($var.list(201,202), target.getAll(K2));
        assertContentEquals($var.list(301), target.getAll(K3));
        assertContentEquals($var.list(), target.getAll(K4));
    }
    
    @TestContract
    public void logicGetAll(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(4).key();
        K K3 = $var.item(6).key();
        K K4 = $var.item(9).key();
        
        target.putAll(K1, Arrays.asList($var.list(1,2).values()) );
        target.putAll(K2, Arrays.asList($var.list(1,2,3,4,5).values()));
        target.putAll(K3, Arrays.asList($var.list(1,3,4,6).values()));
        target.putAll(K4, Arrays.asList($var.list(1,2,4,7).values()));
        
        assertContentEquals($var.list(1,2), target.getAll(K1));
        assertContentEquals($var.list(1,2,3,4,5), target.getAll(K2));
        assertContentEquals($var.list(1,3,4,6), target.getAll(K3));
        assertContentEquals($var.list(1,2,4,7), target.getAll(K4));
    }
    
    @TestContract
    public void basicGetAll_Fold(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();
        K K4 = $var.item(4).key();
        
        init(target);
        
        Comparator<V> cmp = $var.values().comparator();
        Fold<V> fold = (a,b)->CompareUtils.higher(cmp,a,b);
        
        V V1 = fold.apply($var.list(101, 102, 103).values());
        V V2 = fold.apply($var.list(201, 202).values());
        V V3 = fold.apply($var.list(301).values());
        
        assertEquals(V1, target.get(K1, fold));
        assertEquals(V2, target.get(K2, fold));
        assertEquals(V3, target.get(K3, fold));
        
        assertThrows(NoSuchElementException.class, ()-> {
            target.get(K4, fold);
        });
    }
    
    @TestContract
    public void basicRemoveAll(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();
        K K4 = $var.item(4).key();
        
        init(target);
        assertSize(6, target);
        
        assertContentEquals($var.list(101,102,103), target.removeAll(K1));
        assertSize(3, target);
        
        assertContentEquals($var.list(201,202), target.removeAll(K2));
        assertSize(1, target);
        
        assertContentEquals($var.list(301), target.removeAll(K3));
        assertSize(0, target);
        
        assertContentEquals($var.list(), target.removeAll(K4));
    }
    
    @TestContract
    public void basicContainsEntry(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();
        K K4 = $var.item(4).key();
        
        init(target);
        
        assertTrue(target.containsEntry(K1, $var.item(101).value()));
        assertTrue(target.containsEntry(K1, $var.item(102).value()));
        assertTrue(target.containsEntry(K1, $var.item(103).value()));
        assertFalse(target.containsEntry(K1, $var.item(104).value()));
        
        assertTrue(target.containsEntry(K2, $var.item(201).value()));
        assertTrue(target.containsEntry(K2, $var.item(202).value()));
        assertFalse(target.containsEntry(K2, $var.item(203).value()));
        
        assertTrue(target.containsEntry(K3, $var.item(301).value()));
        assertFalse(target.containsEntry(K3, $var.item(302).value()));
        
        assertFalse(target.containsEntry(K4, $var.item(101).value()));
    }
    
    @TestContract
    public void basicPutAll(MultiMap<K,V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();
        K K4 = $var.item(4).key();
        
        target.putAll(K1, Arrays.asList($var.list(101,103).values()));
        target.putAll(K1, Arrays.asList($var.list(102).values()));
        target.putAll(K2, Arrays.asList($var.list(201,202).values()));
        target.putAll(K3, Arrays.asList($var.list(301).values()));
        
        assertTrue(target.containsEntry(K1, $var.item(101).value()));
        assertTrue(target.containsEntry(K1, $var.item(102).value()));
        assertTrue(target.containsEntry(K1, $var.item(103).value()));
        assertFalse(target.containsEntry(K1, $var.item(104).value()));
        
        assertTrue(target.containsEntry(K2, $var.item(201).value()));
        assertTrue(target.containsEntry(K2, $var.item(202).value()));
        assertFalse(target.containsEntry(K2, $var.item(203).value()));
        
        assertTrue(target.containsEntry(K3, $var.item(301).value()));
        assertFalse(target.containsEntry(K3, $var.item(302).value()));
        
        assertFalse(target.containsEntry(K4, $var.item(101).value()));
    }
    
    private void init(MultiMap<K, V> target) {
        K K1 = $var.item(1).key();
        K K2 = $var.item(2).key();
        K K3 = $var.item(3).key();

        target.put(K1, $var.item(101).value());
        target.put(K1, $var.item(102).value());
        target.put(K1, $var.item(103).value());
        target.put(K2, $var.item(201).value());
        target.put(K2, $var.item(202).value());
        target.put(K3, $var.item(301).value());
    }
    
    private void assertContentEquals(TItems<K,V> ai, Collection<V> b) {
        List<V> a = Arrays.asList(ai.values());
        assertEquals("assertContentEquals#size", a.size(), b.size());
        assertEquals("assertContentEquals#isEmpty", a.isEmpty(), b.isEmpty());
        assertTrue("assertContentEquals", a.containsAll(b) && b.containsAll(a));
    }
    
	private void assertSize(int size, Map<K,V> target) {
		assertEquals(size, target.size());
		assertEquals(size, target.entrySet().size());
		assertEquals(size, target.keySet().size());
		assertEquals(size, target.values().size());
	}
    
}
