
package de.ugoe.cs.quest.ui;

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 java.util.List;

import org.junit.*;

import de.ugoe.cs.quest.eventcore.Event;
import de.ugoe.cs.quest.usageprofiles.IStochasticProcess;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
 * 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 testGetAllSequencesNames_1() {
        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));

        String keySeq1 = "seq1";
        Collection<List<Event>> seq1 = new LinkedList<List<Event>>();
        List<Event> subseq1 = new LinkedList<Event>();
        subseq1.add(Event.STARTEVENT);
        seq1.add(subseq1);

        String keySeq2 = "seq2";
        Collection<List<Event>> seq2 = new LinkedList<List<Event>>();
        List<Event> subseq2 = new LinkedList<Event>();
        subseq2.add(Event.STARTEVENT);
        seq2.add(subseq2);

        assertFalse(instance.addData(keySeq1, seq1));
        assertFalse(instance.addData(keySeq2, seq2));

        Collection<String> expected = new LinkedList<String>();
        expected.add(keySeq1);
        expected.add(keySeq2);

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

    @Test
    public void testGetAllSequencesNames_2() {
        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));

        String keySeq1 = "seq1";
        Collection<List<Event>> seq1 = new LinkedList<List<Event>>();
        List<Event> subseq1 = new LinkedList<Event>();
        subseq1.add(Event.STARTEVENT);
        seq1.add(subseq1);

        String keySeq2 = "seq2";
        Collection<List<Event>> seq2 = new LinkedList<List<Event>>();

        assertFalse(instance.addData(keySeq1, seq1));
        assertFalse(instance.addData(keySeq2, seq2));

        Collection<String> expected = new LinkedList<String>();
        expected.add(keySeq1);

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

    @Test
    public void testGetAllSequencesNames_3() {
        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>();

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

    @Test
    public void testGetAllModelNames_1() {
        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));

        String modelKey1 = "model1";
        IStochasticProcess model1 = mock(IStochasticProcess.class);
        String modelKey2 = "model2";
        IStochasticProcess model2 = mock(IStochasticProcess.class);

        assertFalse(instance.addData(modelKey1, model1));
        assertFalse(instance.addData(modelKey2, model2));

        Collection<String> expected = new LinkedList<String>();
        expected.add(modelKey1);
        expected.add(modelKey2);

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

    @Test
    public void testGetAllModelNames_2() {
        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>();

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

    @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();
    }

}
