package br.uem.din.architectureEvolution.representation;

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.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import br.uem.din.architectureEvolution.helpers.ModelHelper;
import br.uem.din.architectureEvolution.helpers.ModelHelperFactory;

public class ArchitectureTest {

	private static Architecture architecture;
	private static String filePath;

	@BeforeClass
	public static void beforeClass() throws Exception{
		ModelHelper modelHelper = ModelHelperFactory.getModelHelper();
		filePath = modelHelper.findResource("br/uem/din/architectureEvolution/resources/AGMBusiness.xmi");
	}

	@Before
	public void setUp() throws Exception {
		architecture = new Architecture(filePath);
	}

	@After
	public void tearDown() throws Exception {
		architecture = null;
	}

	@Test
	public void addConcernTest() {
		assertEquals("Number of concerns", 3, architecture.getConcerns().size());
		
		for (Component comp : architecture.getComponents().values()) {
			comp.addConcern("teste1");
			break;
		}
		
		assertEquals("Number of concerns", 4, architecture.getConcerns().size());
		assertNotNull(architecture.getOrCreateConcernByName("teste1"));
		
		for (Class class_ : architecture.getClasses().values()) {
			class_.addConcern("teste1");
			class_.addConcern("teste2");
			break;
		}
		
		assertEquals("Number of concerns", 5, architecture.getConcerns().size());
		assertNotNull(architecture.getOrCreateConcernByName("teste1"));
		assertNotNull(architecture.getOrCreateConcernByName("teste2"));
	}
	
	@Test
	public void createAndRemoveComponentTest() throws Exception {
		assertEquals(2, architecture.getComponents().size());
		
		Component testComponent = architecture.createComponent("TestComponent");

		assertNotNull(testComponent);
		assertNotNull(testComponent.getModelElement());
		assertEquals("TestComponent", testComponent.getName());
		assertEquals(3, architecture.getComponents().size());
		
		Component found = architecture.findComponentByName("TestComponent");
		
		assertEquals(testComponent, found);
		
		architecture.removeComponent(testComponent);
		
		assertEquals(2, architecture.getComponents().size());
		
		found = architecture.findComponentByName("TestComponent");
		
		assertNull(found);
	}

	@Test
	public void createAndRemoveInterfaceTest() throws Exception {
		assertEquals(1, architecture.getInterfaces().size());
		
		Interface testInterface = architecture.createInterface("TestInterface");

		assertNotNull(testInterface);
		assertNotNull(testInterface.getModelElement());
		assertEquals("TestInterface", testInterface.getName());
		assertEquals(2, architecture.getInterfaces().size());
		
		Interface found = architecture.findInterfaceByName("TestInterface");
		
		assertEquals(testInterface, found);
		
		architecture.removeInterface(testInterface);
		
		assertEquals(1, architecture.getInterfaces().size());
		
		found = architecture.findInterfaceByName("TestInterface");
		
		assertNull(found);
	}
	
	@Test
	public void createClassTest() throws Exception {
		assertEquals(18, architecture.getClasses().size());
		
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class testClass = component.createClass("TestClass");
		
		assertNotNull(testClass);
		assertNotNull(testClass.getModelElement());
		assertEquals("TestClass", testClass.getName());
		assertEquals(19, architecture.getClasses().size());
		
		Class found = architecture.findClassByName("TestClass");
		
		assertEquals(testClass, found);
		
		component.removeClass(testClass);
		
		assertEquals(18, architecture.getClasses().size());
		
		found = architecture.findClassByName("TestClass");
		
		assertNull(found);
	}
	
	@Test
	public void createOperationTest() throws Exception {
		Interface interface_ = null;
		for (Interface i : architecture.getInterfaces().values()) {
			interface_ = i;
			break;
		}
		
		assertEquals(0, interface_.getOperations().size());
		
		Operation testOperation = interface_.createOperation("testOperation");
		
		assertNotNull(testOperation);
		assertNotNull(testOperation.getModelElement());
		assertNotNull(testOperation.getModelElement().getXMIID());
		assertEquals("testOperation", testOperation.getName());
		assertEquals(1, interface_.getOperations().size());
	}
	
	@Test
	public void createMethodTest() throws Exception {
		Class class_ = null;
		for (Class c : architecture.getClasses().values()) {
			class_ = c;
			break;
		}
		
		assertEquals(0, class_.getMethods().size());
		
		Method testMethod = class_.createMethod("testMethod", "String");
		
		assertNotNull(testMethod);
		assertNotNull(testMethod.getModelElement());
		assertNotNull(testMethod.getModelElement().getXMIID());
		assertEquals("testMethod", testMethod.getName());
		assertEquals(1, class_.getMethods().size());
	}
	
	@Test
	public void createAttributeTest() throws Exception {
		Class class_ = null;
		for (Class c : architecture.getClasses().values()) {
			class_ = c;
			break;
		}
		
		assertEquals(0, class_.getAttributes().size());
		
		Attribute testAttribute = class_.createAttribute("testAttribute", "String");
		
		assertNotNull(testAttribute);
		assertNotNull(testAttribute.getModelElement());
		assertNotNull(testAttribute.getModelElement().getXMIID());
		assertEquals("testAttribute", testAttribute.getName());
		assertEquals(1, class_.getAttributes().size());
	}
	
	@Test
	public void abstractionInterElementRelationshipTest() throws Exception {
		Interface interface_ = null;
		for (Interface i : architecture.getInterfaces().values()) {
			interface_ = i;
			break;
		}
		Component component = architecture.createComponent("TestComponent");
		
		AbstractionInterElementRelationship abstraction = new AbstractionInterElementRelationship(interface_, component, architecture);
		
		assertNotNull(abstraction);
		assertNotNull(abstraction.getModelElement());
		assertEquals(2, architecture.getInterElementRelationships().size());
		
		architecture.addInterElementRelationship(abstraction);
		
		assertEquals(3, architecture.getInterElementRelationships().size());
	}
	
	@Test
	public void dependencyComponentInterfaceRelationshipTest() throws Exception {
		Interface interface_ = null;
		for (Interface i : architecture.getInterfaces().values()) {
			interface_ = i;
			break;
		}
		Component component = architecture.createComponent("TestComponent");
		
		DependencyComponentInterfaceRelationship abstraction = new DependencyComponentInterfaceRelationship(interface_, component, architecture);
		
		assertNotNull(abstraction);
		assertNotNull(abstraction.getModelElement());
		assertEquals(2, architecture.getInterElementRelationships().size());
		
		architecture.addInterElementRelationship(abstraction);
		
		assertEquals(3, architecture.getInterElementRelationships().size());
	}
	
	@Test
	public void generalizationInterClassRelationshipTest() throws Exception {
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class class1 = component.createClass("Class1");
		Class class2 = component.createClass("Class2");
		
		GeneralizationInterClassRelationship generalization = new GeneralizationInterClassRelationship(class1, class2, architecture);
		
		assertNotNull(generalization);
		assertNotNull(generalization.getModelElement());
		assertEquals(16, architecture.getInterClassRelationships().size());
		
		architecture.addInterClassRelationship(generalization);
				
		assertEquals(17, architecture.getInterClassRelationships().size());
	}
	
	@Test
	public void associationInterClassRelationshipTest() throws Exception {
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class class1 = component.createClass("Class1");
		Class class2 = component.createClass("Class2");
		
		AssociationInterClassRelationship association = new AssociationInterClassRelationship(class1, class2, architecture);
		
		assertNotNull(association);
		assertNotNull(association.getModelElement());
		assertEquals(16, architecture.getInterClassRelationships().size());
		
		architecture.addInterClassRelationship(association);
				
		assertEquals(17, architecture.getInterClassRelationships().size());
	}
	
	@Test
	public void moveClassToComponentTest() throws Exception {
		Component[] components = architecture.getComponents().values().toArray(new Component[0]);
		Component component1 = components[0];
		Component component2 = components[1];
		
		Class testClass = component1.createClass("TestClass");
		
		component1.moveClassToComponent(testClass, component2);
		
		Class foundInComponent1 = component1.findClassByName("TestClass");
		assertNull(foundInComponent1);
		
		Class foundInComponent2 = component2.findClassByName("TestClass");
		assertNotNull(foundInComponent2);
	}
	
	@Test
	public void moveOperationToInterfaceTest() throws Exception {
		Interface testInterface1 = architecture.createInterface("TestInterface1");
		Interface testInterface2 = architecture.createInterface("TestInterface2");
		
		Operation testOperation = testInterface1.createOperation("testOperation");
		
		testInterface1.moveOperationToInterface(testOperation, testInterface2);
		
		assertFalse(testInterface1.getOperations().contains(testOperation));
		assertTrue(testInterface2.getOperations().contains(testOperation));
	}
	
	@Test
	public void moveMethodToClassTest() throws Exception {
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class testClass1 = component.createClass("TestClass1");
		Class testClass2 = component.createClass("TestClass2");
		
		Method testMethod = testClass1.createMethod("testMethod", "String");
		
		testClass1.moveMethodToClass(testMethod, testClass2);
		
		assertFalse(testClass1.getMethods().values().contains(testMethod));
		assertTrue(testClass2.getMethods().values().contains(testMethod));
	}
	
	@Test
	public void moveAttributeToClassTest() throws Exception {
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class testClass1 = component.createClass("TestClass1");
		Class testClass2 = component.createClass("TestClass2");
		
		Attribute testAttribute = testClass1.createAttribute("testAttribute", "String");
		
		testClass1.moveAttributeToClass(testAttribute, testClass2);
		
		assertFalse(testClass1.getAttributes().values().contains(testAttribute));
		assertTrue(testClass2.getAttributes().values().contains(testAttribute));
	}
	
	@Test
	public void removeImplementedInterfaceFromComponentTest() throws Exception {
		AbstractionInterElementRelationship relationship = null;
		for (AbstractionInterElementRelationship r : architecture.getAbstractionInterElementRelationships()) {
			relationship = r;
			break;
		}
		
		Interface interface_ = relationship.getParent();
		Component component = relationship.getChild();
		
		assertEquals(1, architecture.getAbstractionInterElementRelationships().size());
		assertTrue(component.getImplementedInterfaces().contains(interface_));
		
		architecture.removeImplementedInterfaceFromComponent(interface_, component);
		
		assertEquals(0, architecture.getAbstractionInterElementRelationships().size());
		
		assertFalse(component.getImplementedInterfaces().contains(interface_));
	}
	
	@Test
	public void addImplementedInterfaceToComponentTest() throws Exception {
		Interface interface_ = null;
		for (Interface i : architecture.getInterfaces().values()) {
			interface_ = i;
			break;
		}
		Component component = architecture.createComponent("TestComponent");
		
		assertEquals(1, architecture.getAbstractionInterElementRelationships().size());
		assertFalse(component.getImplementedInterfaces().contains(interface_));
		
		architecture.addImplementedInterfaceToComponent(interface_, component);
		
		assertEquals(2, architecture.getAbstractionInterElementRelationships().size());
		
		assertTrue(component.getImplementedInterfaces().contains(interface_));
	}
	
	@Test
	public void removeRequiredInterfaceFromComponentTest() throws Exception {
		DependencyComponentInterfaceRelationship relationship = null;
		for (DependencyComponentInterfaceRelationship r : architecture.getDependencyComponentInterfaceRelationships()) {
			relationship = r;
			break;
		}
		
		Interface interface_ = relationship.getInterface();
		Component component = relationship.getComponent();
		
		assertEquals(1, architecture.getDependencyComponentInterfaceRelationships().size());
		assertTrue(component.getRequiredInterfaces().contains(interface_));
		
		architecture.removeRequiredInterfaceFromComponent(interface_, component);
		
		assertEquals(0, architecture.getDependencyComponentInterfaceRelationships().size());
		
		assertFalse(component.getRequiredInterfaces().contains(interface_));
	}
	
	@Test
	public void addRequiredInterfaceToComponentTest() throws Exception {
		Interface interface_ = null;
		for (Interface i : architecture.getInterfaces().values()) {
			interface_ = i;
			break;
		}
		Component component = architecture.createComponent("TestComponent");
		
		assertEquals(1, architecture.getDependencyComponentInterfaceRelationships().size());
		assertFalse(component.getRequiredInterfaces().contains(interface_));
		
		architecture.addRequiredInterfaceToComponent(interface_, component);
		
		assertEquals(2, architecture.getDependencyComponentInterfaceRelationships().size());
		
		assertTrue(component.getRequiredInterfaces().contains(interface_));
	}
	
	@Test
	public void classGetRelationshipsTest() throws Exception {
		Component component = null;
		for (Component c : architecture.getComponents().values()) {
			component = c;
			break;
		}
		
		Class class1 = component.createClass("Class1");
		Class class2 = component.createClass("Class2");
		Class class3 = component.createClass("Class3");
		Class class4 = component.createClass("Class4");
		
		GeneralizationInterClassRelationship generalization = new GeneralizationInterClassRelationship(class3, class1, architecture);
		AssociationInterClassRelationship association = new AssociationInterClassRelationship(class1, class2, architecture);
		DependencyInterClassRelationship dependency = new DependencyInterClassRelationship(class4, class1, architecture);
		architecture.addInterClassRelationship(generalization);
		architecture.addInterClassRelationship(association);
		architecture.addInterClassRelationship(dependency);
				
		assertEquals(3, class1.getRelationships().size());
	}
	
	@Test
	public void interfaceGetImplementors() throws Exception {
		Component component1 = architecture.createComponent("TestComponent1");
		Component component2 = architecture.createComponent("TestComponent2");
		Interface interface1 = architecture.createInterface("TestInterface1");
		
		architecture.addImplementedInterfaceToComponent(interface1, component1);
		architecture.addImplementedInterfaceToComponent(interface1, component2);
		
		assertEquals(2, interface1.getImplementors().size());
	}
	
	@Test
	public void interfaceGetDependents() throws Exception {
		Component component1 = architecture.createComponent("TestComponent1");
		Component component2 = architecture.createComponent("TestComponent2");
		Interface interface1 = architecture.createInterface("TestInterface1");
		
		architecture.addRequiredInterfaceToComponent(interface1, component1);
		architecture.addRequiredInterfaceToComponent(interface1, component2);
		
		assertEquals(2, interface1.getDependents().size());
	}
}
