package de.ugoe.cs.util.console;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.LinkedList;

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

import de.ugoe.cs.util.console.GlobalDataContainer;

/**
 * The class <code>WindowsEventTest</code> contains tests for the class
 * <code>{@link WindowsEvent}</code>.
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class GlobalDataContainerTest {

    private static void assertCollectionContent(Collection<?> c1, Collection<?> c2) {
        assertEquals(c1.size(), c2.size());
        for (Object obj : c1) {
            assertTrue(c2.contains(obj));
        }
    }

    @Test
    public void testGlobalDataContainer_1() throws Exception {
        // assure that all constructors are private
        Constructor<?>[] constructors = GlobalDataContainer.class.getDeclaredConstructors();
        for (int i = 0; i < constructors.length; i++) {
            int modifier = constructors[i].getModifiers();
            if (modifier != Modifier.PRIVATE) {
                fail(constructors[i].getName() + " - " + Modifier.toString(modifier));
            }
        }
    }

    @Test
    public void testGetInstance_1() throws Exception {
        GlobalDataContainer instance1 = GlobalDataContainer.getInstance();
        GlobalDataContainer instance2 = GlobalDataContainer.getInstance();
        assertNotNull(instance1);
        assertSame(instance1, instance2);
    }

    @Test
    public void testDatahandling_1() throws Exception {
        // test is data can be added and retrieved as expected
        GlobalDataContainer instance = GlobalDataContainer.getInstance();

        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        assertEquals(data1, instance.getData(key1));
        assertEquals(data2, instance.getData(key2));
        assertEquals(data3, instance.getData(key3));
        assertEquals(data4, instance.getData(key4));
        assertEquals(data5, instance.getData(key5));
    }

    @Test
    public void testDatahandling_2() throws Exception {
        // test is data can be added, overwritten, and retrieved as expected
        GlobalDataContainer instance = GlobalDataContainer.getInstance();

        String key1 = "key1";
        String data1 = "data1";
        String data1changed = "data1changed";
        String key2 = "key2";
        String data2 = "data2";
        String data2changed = "data2changed";
        String key3 = "key3";
        Integer data3 = 1;
        Integer data3changed = 11;
        String key4 = "key4";
        Integer data4 = 2;
        Integer data4changed = 22;
        String key5 = "key5";
        Integer data5 = 3;
        Integer data5changed = 33;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        assertTrue(instance.addData(key1, data1changed));
        assertTrue(instance.addData(key2, data2changed));
        assertTrue(instance.addData(key3, data3changed));
        assertTrue(instance.addData(key4, data4changed));
        assertTrue(instance.addData(key5, data5changed));

        assertEquals(data1changed, instance.getData(key1));
        assertEquals(data2changed, instance.getData(key2));
        assertEquals(data3changed, instance.getData(key3));
        assertEquals(data4changed, instance.getData(key4));
        assertEquals(data5changed, instance.getData(key5));
    }

    @Test
    public void testDatahandling_3() throws Exception {
        // test is data can be removed as expected
        GlobalDataContainer instance = GlobalDataContainer.getInstance();

        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        assertFalse(instance.removeData("keyNotFound"));
        assertTrue(instance.removeData(key1));

        assertNull(instance.getData(key1));
    }

    @Test
    public void testDatahandling_4() throws Exception {
        // test is data can be removed as expected
        GlobalDataContainer instance = GlobalDataContainer.getInstance();

        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        assertNull(instance.getData("keyNotFound"));
    }

    @Test
    public void testReset_1() throws Exception {
        // check if data container is empty after reset
        GlobalDataContainer instance = GlobalDataContainer.getInstance();
        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        instance.reset();

        assertNull(instance.getData(key1));
        assertNull(instance.getData(key2));
        assertNull(instance.getData(key3));
        assertNull(instance.getData(key4));
        assertNull(instance.getData(key5));
    }

    @Test
    public void testGetAllKeys_1() throws Exception {
        // check if data container is empty after reset
        GlobalDataContainer instance = GlobalDataContainer.getInstance();
        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        Collection<String> expected = new LinkedList<String>();
        expected.add(key1);
        expected.add(key2);
        expected.add(key3);
        expected.add(key4);
        expected.add(key5);

        assertCollectionContent(expected, instance.getAllKeys());
    }

    @Test
    public void testSerialization_1() throws Exception {
        GlobalDataContainer instance = GlobalDataContainer.getInstance();
        String key1 = "key1";
        String data1 = "data1";
        String key2 = "key2";
        String data2 = "data2";
        String key3 = "key3";
        Integer data3 = 1;
        String key4 = "key4";
        Integer data4 = 2;
        String key5 = "key5";
        Integer data5 = 3;

        String key6 = "key6";
        Double data6 = 6.0d;

        assertFalse(instance.addData(key1, data1));
        assertFalse(instance.addData(key2, data2));
        assertFalse(instance.addData(key3, data3));
        assertFalse(instance.addData(key4, data4));
        assertFalse(instance.addData(key5, data5));

        // serialize
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(instance);
        oos.close();

        instance.reset();
        assertFalse(instance.addData(key6, data6));

        // deserialize
        byte[] pickled = out.toByteArray();
        InputStream in = new ByteArrayInputStream(pickled);
        ObjectInputStream ois = new ObjectInputStream(in);
        ois.readObject();
        ois.close();

        // test the result
        assertEquals(data1, instance.getData(key1));
        assertEquals(data2, instance.getData(key2));
        assertEquals(data3, instance.getData(key3));
        assertEquals(data4, instance.getData(key4));
        assertEquals(data5, instance.getData(key5));
        assertNull(instance.getData(key6));
    }

    @Before
    public void setUp() {
        GlobalDataContainer.getInstance().reset();
    }

}
