package de.duehl.basics.collections;

/*
 * Copyright 2019 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import static org.junit.Assert.*;

import java.util.List;
import java.util.Set;

import org.junit.Test;

public class OrderedNonOverwritingMapTest {

    @Test
    public void emptySizeAfterCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        int actual = map.size();
        int expected = 0;
        assertEquals(expected, actual);
    }

    @Test
    public void sizeAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        int actual = map.size();
        int expected = 4;
        assertEquals(expected, actual);
    }

    @Test (expected = IllegalArgumentException.class)
    public void addingTwoIdenticalKeys() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("eins", 2);
    }

    @Test (expected = IllegalArgumentException.class)
    public void addingNullKey() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put(null, 1);
    }

    @Test (expected = IllegalArgumentException.class)
    public void addingNullValue() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", null);
    }

    @Test
    public void isEmptyAfterCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        assertTrue(map.isEmpty());
    }

    @Test
    public void isNotEmptyAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        assertFalse(map.isEmpty());
    }

    @Test
    public void containsKeyAfterCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        assertFalse(map.containsKey("eins"));
    }

    @Test
    public void containsKeyAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        assertTrue(map.containsKey("eins"));
        assertTrue(map.containsKey("zwei"));
        assertTrue(map.containsKey("drei"));
        assertTrue(map.containsKey("vier"));
        assertFalse(map.containsKey("fünf"));
    }

    @Test
    public void containsValueAfterCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        assertFalse(map.containsValue(1));
    }

    @Test
    public void containsValueAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        assertTrue(map.containsValue(1));
        assertTrue(map.containsValue(2));
        assertFalse(map.containsValue(3));
        assertTrue(map.containsValue(4));
        assertFalse(map.containsValue(5));
    }

    @Test (expected = IllegalArgumentException.class)
    public void getAfterCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.get("eins");
    }

    @Test
    public void getAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        assertEquals(Integer.valueOf(1), map.get("eins"));
        assertEquals(Integer.valueOf(2), map.get("zwei"));
        assertEquals(Integer.valueOf(1), map.get("drei"));
        assertEquals(Integer.valueOf(4), map.get("vier"));
    }

    @Test
    public void clearAfterAddingAFewElementsCreate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);
        map.clear();
        assertTrue(map.isEmpty());
    }

    @Test
    public void iterate() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);

        int index = 0;
        for (String key : map.getOrderedKeys()) {
            int value = map.get(key);
            //System.out.println(index + " -> key = " + key + ", value = " + value);
            if (0 == index) {
                assertEquals("eins", key);
                assertEquals(1, value);
            }
            else if (1 == index) {
                assertEquals("zwei", key);
                assertEquals(2, value);
            }
            else if (2 == index) {
                assertEquals("drei", key);
                assertEquals(1, value);
            }
            else if (3 == index) {
                assertEquals("vier", key);
                assertEquals(4, value);
            }
            ++index;
        }
    }

    @Test
    public void keySetAndOrderedKeyList() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);

        Set<String> keySet = map.keySet();
        List<String> orderedKeys = map.getOrderedKeys();
        assertEquals(keySet.size(), orderedKeys.size());

        for (String key : orderedKeys) {
            assertTrue(keySet.contains(key));
        }
    }

    @Test
    public void addOrderedNonOverwritingMap() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 3);
        map.put("vier", 4);
        int originalSize = map.size();

        OrderedNonOverwritingMap<String, Integer> otherMap = new OrderedNonOverwritingMap<>();
        otherMap.put("fünf", 5);
        otherMap.put("sechs", 6);
        otherMap.put("sieben", 7);

        map.addOrderedNonOverwritingMap(otherMap);
        int newSize = map.size();
        assertEquals(newSize, 7);

        assertEquals(newSize, otherMap.size() + originalSize);

        List<String> orderedKeys = map.getOrderedKeys();
        assertEquals(newSize, orderedKeys.size());

        assertEquals("eins", orderedKeys.get(0));
        assertEquals("zwei", orderedKeys.get(1));
        assertEquals("drei", orderedKeys.get(2));
        assertEquals("vier", orderedKeys.get(3));
        assertEquals("fünf", orderedKeys.get(4));
        assertEquals("sechs", orderedKeys.get(5));
        assertEquals("sieben", orderedKeys.get(6));

        assertEquals(Integer.valueOf(1), map.get(orderedKeys.get(0)));
        assertEquals(Integer.valueOf(2), map.get(orderedKeys.get(1)));
        assertEquals(Integer.valueOf(3), map.get(orderedKeys.get(2)));
        assertEquals(Integer.valueOf(4), map.get(orderedKeys.get(3)));
        assertEquals(Integer.valueOf(5), map.get(orderedKeys.get(4)));
        assertEquals(Integer.valueOf(6), map.get(orderedKeys.get(5)));
        assertEquals(Integer.valueOf(7), map.get(orderedKeys.get(6)));
    }

    @Test (expected = IllegalArgumentException.class)
    public void addOrderedNonOverwritingMapWithIdenticalKey() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 3);
        map.put("vier", 4);

        OrderedNonOverwritingMap<String, Integer> otherMap = new OrderedNonOverwritingMap<>();
        otherMap.put("eins", 5);

        map.addOrderedNonOverwritingMap(otherMap);
    }

    @Test
    public void toStringTest() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);

        String actual = map.toString();
        String expected = "OrderedNonOverwritingMap [eins=1, zwei=2, drei=1, vier=4]";
        assertEquals(expected, actual);
    }

    @Test
    public void toStringWithTitleTest() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 1);
        map.put("vier", 4);

        String actual = map.toString("Foo");
        String expected = "Foo [eins=1, zwei=2, drei=1, vier=4]";
        assertEquals(expected, actual);
    }

    @Test
    public void removeOldestEntry() {
        OrderedNonOverwritingMap<String, Integer> map = new OrderedNonOverwritingMap<>();
        map.put("eins", 1);
        map.put("zwei", 2);
        map.put("drei", 3);
        map.put("vier", 4);

        assertTrue(map.containsKey("eins"));
        assertTrue(map.containsKey("zwei"));
        assertTrue(map.containsKey("drei"));
        assertTrue(map.containsKey("vier"));
        assertFalse(map.containsKey("fünf"));

        assertTrue(map.containsValue(1));
        assertTrue(map.containsValue(2));
        assertTrue(map.containsValue(3));
        assertTrue(map.containsValue(4));
        assertFalse(map.containsValue(5));

        map.removeOldestEntry();

        assertFalse(map.containsKey("eins"));
        assertTrue(map.containsKey("zwei"));
        assertTrue(map.containsKey("drei"));
        assertTrue(map.containsKey("vier"));
        assertFalse(map.containsKey("fünf"));

        assertFalse(map.containsValue(1));
        assertTrue(map.containsValue(2));
        assertTrue(map.containsValue(3));
        assertTrue(map.containsValue(4));
        assertFalse(map.containsValue(5));
    }

}
