package gr.uom.java.ast.visualization;


import gr.uom.java.ast.FieldInstructionObject;
import gr.uom.java.ast.MethodInvocationObject;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;

public class FeatureEnvyDiagram {

	private ScalableFreeformLayeredPane root;
	private FreeformLayer primary;
	private ConnectionLayer connections;
	private List<JConnection> connectionList= new ArrayList<JConnection>();

	public FeatureEnvyDiagram(FeatureEnvyVisualizationData data) {

		// Create a root figure and simple layout to contain all other figures

		root = new ScalableFreeformLayeredPane();
		primary = new FreeformLayer();
		primary.setLayoutManager(new FreeformLayout());
		root.setFont(Display.getDefault().getSystemFont());
		root.add(primary,"Primary");
		connections = new ConnectionLayer();


		// Create source class
		final ClassFigure source = new ClassFigure(data.getSourceClass().getName(), DecorationConstants.classColor);
		source.setToolTip(new Label("Source Class"));
		source.addThreeCompartments();

		// Create target class
		final ClassFigure target = new ClassFigure(data.getTargetClass().getName(), DecorationConstants.classColor);
		target.addTwoCompartments();
		target.setToolTip(new Label("Target Class"));

		// Create Extract Method figure that goes in the middle
		EntityFigure extractMethod = new EntityFigure(data.getMethodToBeMoved().getName(), DecorationConstants.METHOD, false);
		new ClassFigureMover(extractMethod);
		extractMethod.setLabelAlignment(2);
		extractMethod.setToolTip(new Label("Method to be moved"));
		Font font = DecorationConstants.normalFont;
		extractMethod.setFont(font);

		// Create Extract Method figure that goes in Source Class
		EntityFigure extractMethod1 = new EntityFigure(data.getMethodToBeMoved().getName(), DecorationConstants.METHOD, true);
		extractMethod1.setFont(font);
		extractMethod1.setLabelAlignment(2);
		extractMethod1.setBorder(new SimpleRaisedBorder(3));
		source.getExtractMethodCompartment().add(extractMethod1);


		// Determines interval of weight, ONLY needed when extract method is in Source class
		int size = data.getSourceMethodInvocationMap().size();
		float interval1 = 0.1f/size;

		float weight = 0.3f;

		// Creates Connections for SOURCE methods
		for(Entry<MethodInvocationObject, Integer> entry : data.getSourceMethodInvocationMap().entrySet()){

			MethodInvocationObject method = entry.getKey();
			Integer occurences = entry.getValue();

			weight=weight - interval1;

			//EntityFigure methodFigure = new EntityFigure(method.toString(),EntityFigure.METHOD);
			EntityFigure methodFigure = new EntityFigure(method.getSignature(),DecorationConstants.METHOD, true);
			source.getMethodsCompartment().add(methodFigure);
			JConnection connection=extractMethod.addToSourceMethodConnection(ConnectionType.METHOD_CALL_SOURCE,methodFigure, occurences);
			connectionList.add(connection);
			connections.add(connection);

		}

		// Creates Connections for TARGET methods
		for(Entry<MethodInvocationObject, Integer> entry : data.getTargetMethodInvocationMap().entrySet()){
			MethodInvocationObject method = entry.getKey();
			Integer occurences = entry.getValue();

			//EntityFigure methodFigure = new EntityFigure(method.toString(), EntityFigure.METHOD);
			EntityFigure methodFigure = new EntityFigure(method.getSignature(), DecorationConstants.METHOD, true);
			target.getMethodsCompartment().add(methodFigure);
			JConnection connection=extractMethod.addToTargetMethodConnection(ConnectionType.METHOD_CALL_TARGET,methodFigure, occurences);
			connectionList.add(connection);
			connections.add(connection);
		}

		// Creates Connections for SOURCE Read fields
		weight = 0.2f;
		float interval2 = 0.1f/data.getSourceFieldReadMap().size();
		for( Entry<FieldInstructionObject, Integer> entry : data.getSourceFieldReadMap().entrySet()){
			FieldInstructionObject field = entry.getKey();
			Integer occurences = entry.getValue();
			weight=weight + interval2;


			//EntityFigure fieldFigure = new EntityFigure(field.toString(),EntityFigure.FIELD);
			EntityFigure fieldFigure = new EntityFigure(field.getName(),DecorationConstants.FIELD, true);
			source.getFieldsCompartment().add(fieldFigure);
			JConnection connection =extractMethod.addToSourceWeakReadConnection(ConnectionType.READ_FIELD_SOURCE,fieldFigure, occurences);
			connectionList.add(connection);
			connections.add(connection);

		}

		// Creates Connections for SOURCE Write methods
		float interval3 = 0.4f/data.getSourceFieldWriteMap().size();
		weight = 0.2f;
		for( Entry<FieldInstructionObject, Integer> entry : data.getSourceFieldWriteMap().entrySet()){
			FieldInstructionObject field = entry.getKey();
			Integer occurences = entry.getValue();
			weight=weight + interval3;

			//EntityFigure fieldFigure = new EntityFigure(field.toString(), EntityFigure.FIELD);
			EntityFigure fieldFigure = new EntityFigure(field.getName(), DecorationConstants.FIELD, true);
			boolean contains = false;

			for(Object child : source.getFieldsCompartment().getChildren()){
				EntityFigure entity = (EntityFigure) child;
				if (entity.getName().equals(fieldFigure.getName()))
					contains = true;
				//JConnection connection = extractMethod.addToSourceWeakWriteConnection(ConnectionType.WRITE_FIELD_SOURCE,entity, occurences);
				JConnection connection = extractMethod.addToSourceBendConnection(ConnectionType.WRITE_FIELD_SOURCE,entity, occurences);
				connectionList.add(connection);
				connections.add(connection);

			}

			if(!contains)	{
				source.getFieldsCompartment().add(fieldFigure);
				JConnection connection = extractMethod.addToSourceWeakWriteConnection(ConnectionType.WRITE_FIELD_SOURCE,fieldFigure, occurences);
				connectionList.add(connection);
				connections.add(connection);
			}

		}

		for( Entry<FieldInstructionObject, Integer> entry : data.getTargetFieldReadMap().entrySet()){
			FieldInstructionObject field = entry.getKey();
			Integer occurences = entry.getValue();

			//EntityFigure fieldFigure = new EntityFigure(field.toString(), EntityFigure.FIELD);
			EntityFigure fieldFigure = new EntityFigure(field.getName(), DecorationConstants.FIELD, true);
			target.getFieldsCompartment().add(fieldFigure);
			JConnection connection =extractMethod.addToTargetReadConnection(ConnectionType.READ_FIELD_TARGET,fieldFigure, occurences);
			connectionList.add(connection);
			connections.add(connection);

		}

		for( Entry<FieldInstructionObject, Integer> entry : data.getTargetFieldWriteMap().entrySet()){
			FieldInstructionObject field = entry.getKey();
			Integer occurences = entry.getValue();

			//EntityFigure fieldFigure = new EntityFigure(field.toString(), EntityFigure.FIELD);
			EntityFigure fieldFigure = new EntityFigure(field.getName(), DecorationConstants.FIELD, true);

			boolean contains = false;
			for(Object child : target.getFieldsCompartment().getChildren()){
				EntityFigure entity = (EntityFigure) child;
				if (entity.getName().equals(fieldFigure.getName())){
					contains = true;
					JConnection connection = extractMethod.addToTargetBendConnection(ConnectionType.WRITE_FIELD_TARGET,entity, occurences);
					connectionList.add(connection);
					connections.add(connection);
				}


			}

			if(!contains)	{
				target.getFieldsCompartment().add(fieldFigure);
				JConnection connection = extractMethod.addToTargetWriteConnection(ConnectionType.WRITE_FIELD_TARGET,fieldFigure, occurences);
				connectionList.add(connection);
				connections.add(connection);
			}

		}

		int classWidth = 300;
		int startPointX = 100;
		int startPointY = 50;
		int methodWidth = 150;
		int gap = 100;
		extractMethod.setMaximumSize(new Dimension(methodWidth,20));

		final Legend legend = new Legend(connectionList, false);
		int legendHeight = 150;
		int legendWidth = 275;
		int legendGap = 200;

		primary.add(source, new Rectangle(startPointX,startPointY,classWidth,-1));
		primary.add(target, new Rectangle(startPointX+classWidth + methodWidth + 2*gap,startPointY,classWidth,-1));
		int sourceH = source.getPreferredSize().height;
		int targetH = target.getPreferredSize().height;
		primary.add(extractMethod, new Rectangle(startPointX+classWidth + gap, startPointY + Math.min(targetH, sourceH)/2 , -1,-1));
		primary.add(legend, new Rectangle(startPointX,sourceH + legendGap, legendWidth, legendHeight));
		root.add(connections, "Connections");

	}

	public ScalableFreeformLayeredPane getRoot() {
		return root;
	}
}

