/***************************************************************************
 * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package kieker.webgui.web.utility;

import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory;
import kieker.analysis.model.analysisMetaModel.MIDisplayConnector;
import kieker.analysis.model.analysisMetaModel.MIProject;
import kieker.analysis.model.analysisMetaModel.MIView;

/**
 * A test for {@link CockpitLayout}.
 * 
 * @author Nils Christian Ehmke
 */
public class CockpitLayoutTest {

	/**
	 * Default constructor. <b>Do not use this constructor. This is just a test class and not to be used outside a JUnit test!</b>
	 */
	public CockpitLayoutTest() {
		// No code necessary
	}

	/**
	 * This test makes sure that the class stores and delivers the layout correctly.
	 */
	@Test
	public void testLayoutPreservation() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		final MIView view1 = factory.createView();
		final MIView view2 = factory.createView();
		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		// Add everything to the cockpit layout
		cockpitLayout.addView(view1);
		cockpitLayout.addView(view2);

		cockpitLayout.addDisplayConnector(view1, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector3, 1);

		cockpitLayout.addDisplayConnector(view2, displayConnector4, 0);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout1 = cockpitLayout.getCurrentLayout(view1);
		final List<List<MIDisplayConnector>> layout2 = cockpitLayout.getCurrentLayout(view2);

		Assert.assertEquals("Invalid layout", 2, layout1.size());
		Assert.assertEquals("Invalid layout", 2, layout2.size());

		Assert.assertEquals("Invalid layout", 2, layout1.get(0).size());
		Assert.assertEquals("Invalid layout", 1, layout1.get(1).size());

		Assert.assertEquals("Invalid layout", 1, layout2.get(0).size());
		Assert.assertEquals("Invalid layout", 0, layout2.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout1.get(0).get(0));
		Assert.assertEquals("Invalid layout", displayConnector2, layout1.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector3, layout1.get(1).get(0));
		Assert.assertEquals("Invalid layout", displayConnector4, layout2.get(0).get(0));
	}

	/**
	 * This test makes sure that display connectors can be moved within the layout.
	 */
	@Test
	public void testMovement() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		final MIView view = factory.createView();

		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		// Add everything to the cockpit layout
		cockpitLayout.addView(view);

		cockpitLayout.addDisplayConnector(view, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector3, 1);
		cockpitLayout.addDisplayConnector(view, displayConnector4, 1);

		// Now move a display connector
		cockpitLayout.moveDisplayConnector(view, displayConnector3, 1, 0, 1);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout = cockpitLayout.getCurrentLayout(view);

		Assert.assertEquals("Invalid layout", 2, layout.size());

		Assert.assertEquals("Invalid layout", 3, layout.get(0).size());
		Assert.assertEquals("Invalid layout", 1, layout.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout.get(0).get(0));
		Assert.assertEquals("Invalid layout", displayConnector2, layout.get(0).get(2));
		Assert.assertEquals("Invalid layout", displayConnector3, layout.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector4, layout.get(1).get(0));
	}

	/**
	 * This test makes sure that display connectors can be moved within the layout but also within the same column.
	 */
	@Test
	public void testSameColumnMovement() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 1);

		final MIView view = factory.createView();

		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		// Add everything to the cockpit layout
		cockpitLayout.addView(view);

		cockpitLayout.addDisplayConnector(view, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector3, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector4, 0);

		// Now move a display connector
		cockpitLayout.moveDisplayConnector(view, displayConnector4, 0, 0, 0);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout = cockpitLayout.getCurrentLayout(view);

		Assert.assertEquals("Invalid layout", 1, layout.size());

		Assert.assertEquals("Invalid layout", 4, layout.get(0).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector2, layout.get(0).get(2));
		Assert.assertEquals("Invalid layout", displayConnector3, layout.get(0).get(3));
		Assert.assertEquals("Invalid layout", displayConnector4, layout.get(0).get(0));
	}

	/**
	 * This test makes sure that display connectors can be removed from the layout.
	 */
	@Test
	public void testDisplayConnectorRemoving() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		final MIView view = factory.createView();

		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		// Add everything to the cockpit layout
		cockpitLayout.addView(view);

		cockpitLayout.addDisplayConnector(view, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view, displayConnector3, 1);
		cockpitLayout.addDisplayConnector(view, displayConnector4, 1);

		// Now remove a display connector
		cockpitLayout.removeDisplayConnector(view, displayConnector3);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout = cockpitLayout.getCurrentLayout(view);

		Assert.assertEquals("Invalid layout", 2, layout.size());

		Assert.assertEquals("Invalid layout", 2, layout.get(0).size());
		Assert.assertEquals("Invalid layout", 1, layout.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout.get(0).get(0));
		Assert.assertEquals("Invalid layout", displayConnector2, layout.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector4, layout.get(1).get(0));
	}

	/**
	 * This test makes sure that views can be removed from the layout.
	 */
	@Test
	public void testViewsRemoving() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		final MIView view1 = factory.createView();
		final MIView view2 = factory.createView();
		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		// Add everything to the cockpit layout
		cockpitLayout.addView(view1);
		cockpitLayout.addView(view2);

		cockpitLayout.addDisplayConnector(view1, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector3, 1);

		cockpitLayout.addDisplayConnector(view2, displayConnector4, 0);

		// Now remove a view
		cockpitLayout.removeView(view1);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout2 = cockpitLayout.getCurrentLayout(view2);

		Assert.assertEquals("Invalid layout", 2, layout2.size());

		Assert.assertEquals("Invalid layout", 1, layout2.get(0).size());
		Assert.assertEquals("Invalid layout", 0, layout2.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector4, layout2.get(0).get(0));

		// The following should throw an error
		try {
			cockpitLayout.getCurrentLayout(view1);
			Assert.fail("Invalid layout");
		} catch (final NullPointerException ex) { // NOPMD (JUnit Test)
		}
	}

	/**
	 * This test makes sure that the layout can be serialized and deserialized.
	 */
	@Test
	public void testSerialization() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		final MIView view1 = factory.createView();
		final MIView view2 = factory.createView();
		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		view1.getDisplayConnectors().add(displayConnector1);
		view1.getDisplayConnectors().add(displayConnector2);
		view1.getDisplayConnectors().add(displayConnector3);

		view2.getDisplayConnectors().add(displayConnector4);

		project.getViews().add(view1);
		project.getViews().add(view2);

		// Add everything to the cockpit layout
		cockpitLayout.addView(view1);
		cockpitLayout.addView(view2);

		cockpitLayout.addDisplayConnector(view1, displayConnector1, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector2, 0);
		cockpitLayout.addDisplayConnector(view1, displayConnector3, 1);

		cockpitLayout.addDisplayConnector(view2, displayConnector4, 0);

		// Now serialize and deserialize the layout
		final CockpitLayout cockpitLayout2 = new CockpitLayout(project, cockpitLayout.serializeToString(), 2);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout1 = cockpitLayout2.getCurrentLayout(view1);
		final List<List<MIDisplayConnector>> layout2 = cockpitLayout2.getCurrentLayout(view2);

		Assert.assertEquals("Invalid layout", 2, layout1.size());
		Assert.assertEquals("Invalid layout", 2, layout2.size());

		Assert.assertEquals("Invalid layout", 2, layout1.get(0).size());
		Assert.assertEquals("Invalid layout", 1, layout1.get(1).size());

		Assert.assertEquals("Invalid layout", 1, layout2.get(0).size());
		Assert.assertEquals("Invalid layout", 0, layout2.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout1.get(0).get(0));
		Assert.assertEquals("Invalid layout", displayConnector2, layout1.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector3, layout1.get(1).get(0));
		Assert.assertEquals("Invalid layout", displayConnector4, layout2.get(0).get(0));
	}

	/**
	 * This test makes sure that the class handles empty layouts correctly.
	 */
	@Test
	public void testEmptyLayout() {
		// Create the necessary components
		final MIAnalysisMetaModelFactory factory = MIAnalysisMetaModelFactory.eINSTANCE;
		final MIProject project = factory.createProject();

		final MIView view1 = factory.createView();
		final MIView view2 = factory.createView();
		final MIDisplayConnector displayConnector1 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector2 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector3 = factory.createDisplayConnector();
		final MIDisplayConnector displayConnector4 = factory.createDisplayConnector();

		view1.getDisplayConnectors().add(displayConnector1);
		view1.getDisplayConnectors().add(displayConnector2);
		view1.getDisplayConnectors().add(displayConnector3);

		view2.getDisplayConnectors().add(displayConnector4);

		project.getViews().add(view1);
		project.getViews().add(view2);

		// Let the class load the project without layout
		final CockpitLayout cockpitLayout = new CockpitLayout(project, null, 2);

		// Now get and check the layout
		final List<List<MIDisplayConnector>> layout1 = cockpitLayout.getCurrentLayout(view1);
		final List<List<MIDisplayConnector>> layout2 = cockpitLayout.getCurrentLayout(view2);

		Assert.assertEquals("Invalid layout", 2, layout1.size());
		Assert.assertEquals("Invalid layout", 2, layout2.size());

		Assert.assertEquals("Invalid layout", 3, layout1.get(0).size());
		Assert.assertEquals("Invalid layout", 0, layout1.get(1).size());

		Assert.assertEquals("Invalid layout", 1, layout2.get(0).size());
		Assert.assertEquals("Invalid layout", 0, layout2.get(1).size());

		Assert.assertEquals("Invalid layout", displayConnector1, layout1.get(0).get(0));
		Assert.assertEquals("Invalid layout", displayConnector2, layout1.get(0).get(1));
		Assert.assertEquals("Invalid layout", displayConnector3, layout1.get(0).get(2));
		Assert.assertEquals("Invalid layout", displayConnector4, layout2.get(0).get(0));
	}

}
