package net.ranides.assira.collection.prototype;

import org.junit.Before;
import org.junit.Test;

import net.ranides.test.ContractTesters;
import net.ranides.test.data.TMaps;
import net.ranides.assira.collection.sets.OpenSet;
import net.ranides.test.data.TMap;

import java.util.*;
import java.util.regex.Pattern;

import static org.junit.Assert.*;

public class PrototypeMapTest {

    private final TMap<Object, Object> $map = TMaps.MAP_SI;

    PrototypeMap map1;
    PrototypeMap map2;
    PrototypeMap map3;
    PrototypeMap map4;

    @Before
    public void init() {
        map1 = new PrototypeMap();
        map2 = new PrototypeMap(map1);
        map3 = new PrototypeMap(map2);
        map4 = new PrototypeMap(map3);

        map1.getDeclaredMap().put("a", 11);
        map1.getDeclaredMap().put("b", 12);
        map2.getDeclaredMap().put("c", 13);
        map2.getDeclaredMap().put("d", 14);
        map3.getDeclaredMap().put("e", 15);
        map3.getDeclaredMap().put("f", 16);
    }

    @Test
    public void testSuite() {
        ContractTesters.runner()
            .param("map!", $map)
            .ignore(Pattern.compile(".*_HC"))
            .function(new int[0], array -> $map.list(array).into(new PrototypeMap()))
            .run();
    }

    @Test
    public void basic() {
        assertEquals(12, map1.get("b"));
        assertEquals(12, map2.get("b"));
        assertEquals(12, map3.get("b"));
        assertEquals(12, map4.get("b"));

        assertEquals(16, map3.get("f"));
        assertEquals(16, map4.get("f"));

        assertTrue(map1.containsKey("b"));
        assertTrue(map4.containsKey("b"));

        assertTrue(map3.containsKey("f"));
        assertTrue(map4.containsKey("f"));

        assertTrue(map1.getDeclaredMap().containsKey("b"));
        assertFalse(map4.getDeclaredMap().containsKey("b"));

        assertTrue(map3.containsKey("f"));
        assertFalse(map4.getDeclaredMap().containsKey("f"));
    }

    @Test
    public void keys() {
        Set<String> keys1 = new OpenSet<>(Arrays.asList("a", "b"));
        Set<String> keys2 = new OpenSet<>(Arrays.asList("a", "b", "c", "d"));
        Set<String> keys3 = new OpenSet<>(Arrays.asList("a", "b", "c", "d", "e", "f"));

        Set<String> dec1 = new OpenSet<>(Arrays.asList("a", "b"));
        Set<String> dec2 = new OpenSet<>(Arrays.asList("c", "d"));
        Set<String> dec3 = new OpenSet<>(Arrays.asList("e", "f"));
        Set<String> dec4 = Collections.emptySet();

        assertEquals(keys1, map1.keySet());
        assertEquals(keys2, map2.keySet());
        assertEquals(keys3, map3.keySet());
        assertEquals(keys3, map4.keySet());

        assertEquals(dec1, map1.getDeclaredMap().keySet());
        assertEquals(dec2, map2.getDeclaredMap().keySet());
        assertEquals(dec3, map3.getDeclaredMap().keySet());
        assertEquals(dec4, map4.getDeclaredMap().keySet());
    }

    @Test
    public void entrySet_remove() {
        List<Map.Entry<Object, Object>> entries = new ArrayList<>(map1.entrySet());

        Iterator<Map.Entry<Object, Object>> a = map1.entrySet().iterator();
        a.next();
        a.remove();

        for (Map.Entry<Object, Object> entry : entries) {
            assertNotNull(entry.getKey());
            assertNotNull(entry.getValue());
        }
    }
}