package net.ranides.test.contracts.collection.maps;

import net.ranides.assira.collection.prototype.*;
import net.ranides.assira.junit.NewAssert;

import java.util.function.Supplier;
import java.util.stream.Collectors;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;

public class GenericMapTester {

    private static final GenericKey<String, Integer>      I_A = GenericKey.of(Integer.class, "a");
    private static final GenericKey<String, String>       S_A = GenericKey.of(String.class, "a");
    private static final GenericKey<String, Number>       N_A = GenericKey.of(Number.class, "a");
    private static final GenericKey<String, Integer>      I_B = GenericKey.of(Integer.class, "b");
    private static final GenericKey<String, Object>       O_B = GenericKey.of(Object.class, "b");
    private static final GenericKey<String, Float>        F_B = GenericKey.of(Float.class, "b");
    private static final GenericKey<String, Double>       D_C = GenericKey.of(Double.class, "c");
    private static final GenericKey<String, CharSequence> C_D = GenericKey.of(CharSequence.class, "d");
    private static final GenericKey<String, CharSequence> C_E = GenericKey.of(CharSequence.class, "e");

    public static void basic(Supplier<GenericMap<String>> factory) {
        GenericMap<String> map1 = factory.get();

//        map.put(a, "Hello"); // compiler error :-)
//        map.put(a, 45.6); // compiler error :-)
//        map.put(c, 1); // compiler error :-)

        map1.put(I_A, 45);
        map1.put(I_B, 32);
        map1.put(D_C, 1.5);
        map1.put(C_D, "text");

        GenericMap<String> map2 = factory.get();
        map2.putAll(map1);

        assertEquals(map1, map2);

        assertEquals((Integer)45, map1.remove(I_A));
        assertNull(map1.remove(I_A));
        assertNull(map1.remove(C_E));

        assertEquals(3, map1.size());
        assertEquals(4, map2.size());

        assertNotEquals(map1, map2);

        assertFalse(map1.containsKey(I_A));
        assertTrue(map1.containsKey(C_D));
        assertTrue(map1.containsValue("text"));
        assertFalse(map1.containsValue(45));
    }

    public static void contains(Supplier<GenericMap<String>> factory) {
        GenericMap<String> map1 = factory.get();

        map1.put(I_A, 45);
        map1.put(I_B, 32);
        map1.put(D_C, 1.5);
        map1.put(C_D, "text");

        assertTrue(map1.containsKey(I_A));
        assertTrue(map1.containsKey(N_A));
        assertFalse(map1.containsKey(S_A));

        assertTrue(map1.containsKey(I_B));
        assertTrue(map1.containsKey(O_B));
        assertFalse(map1.containsKey(F_B));

        assertNotNull(map1.get(I_A));
        assertNotNull(map1.get(N_A));
        assertNull(map1.get(S_A));

        assertNotNull(map1.get(I_B));
        assertNotNull(map1.get(O_B));
        assertNull(map1.get(F_B));
    }

    public static void entries(Supplier<GenericMap<String>> factory) {
        GenericMap<String> map1 = factory.get();

        map1.put(I_A, 45);
        map1.put(I_B, 32);
        map1.put(D_C, 1.5);
        map1.put(C_D, "text");

        assertEquals(4, map1.entrySet().size());

        assertTrue(map1.entrySet().contains(new GenericEntry.Simple<>(I_A, 45)));
        assertFalse(map1.entrySet().contains(new GenericEntry.Simple<>(I_A, 44)));

        boolean found = map1.entrySet().stream()
            .filter(e -> e.getKey().equals(I_B))
            .findFirst()
            .map(e -> e.equals(new GenericEntry.Simple<>(I_B, 32)))
            .orElse(false);

        assertTrue(found);

    }

    public static void views(Supplier<GenericMap<String>> factory) {
        GenericMap<String> map1 = factory.get();

        map1.put(I_A, 45);
        map1.put(I_B, 32);
        map1.put(D_C, 1.5);
        map1.put(C_D, "text");

        assertEquals("1.5,32,45,text", map1.values().stream().map(String::valueOf).sorted().collect(Collectors.joining(",")) );
        assertEquals("a:Integer,b:Integer,c:Double,d:CharSequence", map1.keySet().stream().map(String::valueOf).sorted().collect(Collectors.joining(",")) );
    }

    public static void setEntry(Supplier<GenericMap<String>> factory) {
        GenericMap<String> map1 = factory.get();

        map1.put(I_A, 45);
        map1.put(I_B, 32);
        map1.put(D_C, 1.5);
        map1.put(C_D, "text");

        GenericEntry<String> entry = map1.entrySet().stream()
            .filter(e -> e.getKey().equals(I_B))
            .findFirst()
            .get();

        entry.setValue(88);
        assertEquals((Integer)88, map1.get(I_B));

        NewAssert.assertThrows(ClassCastException.class, () -> {
            entry.setValue("text");
        });
    }
}
