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

}
