//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.diagram.model.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.epf.diagram.model.ActivityDetailDiagram;
import org.eclipse.epf.diagram.model.ModelFactory;
import org.eclipse.epf.diagram.model.ModelPackage;
import org.eclipse.epf.diagram.model.Node;
import org.eclipse.epf.diagram.model.NodeContainer;
import org.eclipse.epf.diagram.model.RoleNode;
import org.eclipse.epf.diagram.model.RoleTaskComposite;
import org.eclipse.epf.diagram.model.TaskNode;
import org.eclipse.epf.diagram.model.WorkProductComposite;
import org.eclipse.epf.diagram.model.util.GraphicalDataHelper;
import org.eclipse.epf.diagram.model.util.INodeChangeListener;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.GraphNode;
import org.eclipse.epf.uma.Property;
import org.eclipse.epf.uma.RoleDescriptor;
import org.eclipse.epf.uma.TaskDescriptor;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.ecore.util.OppositeFeatureNotification;
import org.eclipse.epf.uma.util.AssociationHelper;


/**
 * <!-- begin-user-doc --> An implementation of the model object '<em><b>Role Task Composite</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * </p>
 * 
 * @generated
 */
public class RoleTaskCompositeImpl extends NodeContainerImpl implements
		RoleTaskComposite {
	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 */
	protected RoleTaskCompositeImpl() {
		super();

		methodElementAdapter = new RoleDescriptorAdapter();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	protected EClass eStaticClass() {
		return ModelPackage.eINSTANCE.getRoleTaskComposite();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public NotificationChain eInverseAdd(InternalEObject otherEnd,
			int featureID, Class baseClass, NotificationChain msgs) {
		if (featureID >= 0) {
			switch (eDerivedStructuralFeatureID(featureID, baseClass)) {
			case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
				return ((InternalEList) getIncomingConnections()).basicAdd(
						otherEnd, msgs);
			case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
				return ((InternalEList) getOutgoingConnections()).basicAdd(
						otherEnd, msgs);
			default:
				return eDynamicInverseAdd(otherEnd, featureID, baseClass, msgs);
			}
		}
		if (eContainer != null)
			msgs = eBasicRemoveFromContainer(msgs);
		return eBasicSetContainer(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public NotificationChain eInverseRemove(InternalEObject otherEnd,
			int featureID, Class baseClass, NotificationChain msgs) {
		if (featureID >= 0) {
			switch (eDerivedStructuralFeatureID(featureID, baseClass)) {
			case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
				return ((InternalEList) getIncomingConnections()).basicRemove(
						otherEnd, msgs);
			case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
				return ((InternalEList) getOutgoingConnections()).basicRemove(
						otherEnd, msgs);
			case ModelPackage.ROLE_TASK_COMPOSITE__NODES:
				return ((InternalEList) getNodes()).basicRemove(otherEnd, msgs);
			default:
				return eDynamicInverseRemove(otherEnd, featureID, baseClass,
						msgs);
			}
		}
		return eBasicSetContainer(null, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public Object eGet(EStructuralFeature eFeature, boolean resolve) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.ROLE_TASK_COMPOSITE__OBJECT:
			return getObject();
		case ModelPackage.ROLE_TASK_COMPOSITE__LOCATION:
			return getLocation();
		case ModelPackage.ROLE_TASK_COMPOSITE__WIDTH:
			return new Integer(getWidth());
		case ModelPackage.ROLE_TASK_COMPOSITE__HEIGHT:
			return new Integer(getHeight());
		case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
			return getIncomingConnections();
		case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
			return getOutgoingConnections();
		case ModelPackage.ROLE_TASK_COMPOSITE__READ_ONLY:
			return isReadOnly() ? Boolean.TRUE : Boolean.FALSE;
		case ModelPackage.ROLE_TASK_COMPOSITE__NODES:
			return getNodes();
		}
		return eDynamicGet(eFeature, resolve);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public void eSet(EStructuralFeature eFeature, Object newValue) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.ROLE_TASK_COMPOSITE__OBJECT:
			setObject((Object) newValue);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__LOCATION:
			setLocation((Point) newValue);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__WIDTH:
			setWidth(((Integer) newValue).intValue());
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__HEIGHT:
			setHeight(((Integer) newValue).intValue());
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
			getIncomingConnections().clear();
			getIncomingConnections().addAll((Collection) newValue);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
			getOutgoingConnections().clear();
			getOutgoingConnections().addAll((Collection) newValue);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__READ_ONLY:
			setReadOnly(((Boolean) newValue).booleanValue());
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__NODES:
			getNodes().clear();
			getNodes().addAll((Collection) newValue);
			return;
		}
		eDynamicSet(eFeature, newValue);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public void eUnset(EStructuralFeature eFeature) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.ROLE_TASK_COMPOSITE__OBJECT:
			setObject(OBJECT_EDEFAULT);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__LOCATION:
			setLocation(LOCATION_EDEFAULT);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__WIDTH:
			setWidth(WIDTH_EDEFAULT);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__HEIGHT:
			setHeight(HEIGHT_EDEFAULT);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
			getIncomingConnections().clear();
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
			getOutgoingConnections().clear();
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__READ_ONLY:
			setReadOnly(READ_ONLY_EDEFAULT);
			return;
		case ModelPackage.ROLE_TASK_COMPOSITE__NODES:
			getNodes().clear();
			return;
		}
		eDynamicUnset(eFeature);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public boolean eIsSet(EStructuralFeature eFeature) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.ROLE_TASK_COMPOSITE__OBJECT:
			return OBJECT_EDEFAULT == null ? object != null : !OBJECT_EDEFAULT
					.equals(object);
		case ModelPackage.ROLE_TASK_COMPOSITE__LOCATION:
			return LOCATION_EDEFAULT == null ? location != null
					: !LOCATION_EDEFAULT.equals(location);
		case ModelPackage.ROLE_TASK_COMPOSITE__WIDTH:
			return width != WIDTH_EDEFAULT;
		case ModelPackage.ROLE_TASK_COMPOSITE__HEIGHT:
			return height != HEIGHT_EDEFAULT;
		case ModelPackage.ROLE_TASK_COMPOSITE__INCOMING_CONNECTIONS:
			return incomingConnections != null
					&& !incomingConnections.isEmpty();
		case ModelPackage.ROLE_TASK_COMPOSITE__OUTGOING_CONNECTIONS:
			return outgoingConnections != null
					&& !outgoingConnections.isEmpty();
		case ModelPackage.ROLE_TASK_COMPOSITE__READ_ONLY:
			return readOnly != READ_ONLY_EDEFAULT;
		case ModelPackage.ROLE_TASK_COMPOSITE__NODES:
			return nodes != null && !nodes.isEmpty();
		}
		return eDynamicIsSet(eFeature);
	}

	private static boolean contains(Collection mixedCollection, Object element) {
		for (Iterator iter = mixedCollection.iterator(); iter.hasNext();) {
			if( element == TngUtil.unwrap(iter.next()) ) {
				return true;
			}		
		}
		return false;
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.diagram.model.impl.NodeImpl#setObject(java.lang.Object)
	 */
	public void setObject(Object newObject) {
		super.setObject(newObject);
		
		if (newObject instanceof RoleDescriptor) {
			RoleDescriptor roleDescriptor = (RoleDescriptor) newObject;
			ActivityDetailDiagramImpl diagram = (ActivityDetailDiagramImpl) getDiagram();
			Collection allElements = diagram.getAllBreakdownElements();
			List primaryTaskDescriptors = AssociationHelper.getPrimaryTaskDescriptors(roleDescriptor);
			if (!primaryTaskDescriptors.isEmpty()) { // overriding
																	// RATLC383447
				// use list to maintain order of
				// taskdescriptors.
				List taskDescriptors = new ArrayList();
				taskDescriptors.addAll(primaryTaskDescriptors);
				// taskDescriptors.addAll(roleDescriptor.getPerformsAdditionally());
				// taskDescriptors.addAll(roleDescriptor.getAssistsIn());

				HashSet selectedDescriptors = new HashSet();
				List taskNodes = new ArrayList();
				
				String autolayout = GraphicalDataHelper.getAutoLayoutFlag((ActivityDetailDiagram)getDiagram()); 
				if(autolayout != null){
					int index = 0;
					for (Iterator iter = allElements.iterator(); iter.hasNext();) {
						Object obj = iter.next();
						obj = TngUtil.unwrap(obj);
						if (obj instanceof TaskDescriptor
								&& TngUtil.contains(taskDescriptors, obj)) {
							TaskDescriptor taskDescriptor = (TaskDescriptor) obj;

							// eliminate descriptor that is duplicate,
							// suppressed or filtered out
							//
							if (!selectedDescriptors.contains(taskDescriptor)
							// && contains(allElements, taskDescriptor) // make
							// sure that task descriptor is of the owning
							// activity
							) {
								selectedDescriptors.add(taskDescriptor);
								Node node = GraphicalDataHelper.findNode(
										this, taskDescriptor,
										TaskNode.class);
								if (node == null) {
									node = ModelFactory.eINSTANCE
											.createTaskNode();
									node.setDiagram(getDiagram());
									node.setObject(taskDescriptor);
									((NamedNodeImpl) node)
											.setItemProvider(allElements);
									if(GraphicalDataHelper.findProperty(node.getGraphNode(), 
											GraphicalDataHelper.PROP_INDEX) == null
											|| GraphicalDataHelper.isAutoLayout((ActivityDetailDiagram)getDiagram())){
										GraphicalDataHelper.createProperty(node
											.getGraphNode(),
											GraphicalDataHelper.PROP_INDEX,
											new Integer(index).toString());
										index++;
									}
									
								}
								taskNodes.add(node);
							}
						}
					}
					// Sort the taskNode based on their index if autolayout is
					// false
					if (GraphicalDataHelper.PROP_AUTO_LAYOUT_VALUE_FALSE
							.equals(autolayout)) {
						Collections.sort(taskNodes, comparator);
					}
			  }else{
					for (Iterator iter = taskDescriptors.iterator(); iter.hasNext();) {
						TaskDescriptor taskDescriptor = (TaskDescriptor) iter.next();
						
						// eliminate descriptor that is duplicate, suppressed or filtered out
						//
						if(!selectedDescriptors.contains(taskDescriptor)
								&& contains(allElements, taskDescriptor) // make sure that task descriptor is of the owning activity
						) {
							selectedDescriptors.add(taskDescriptor);					
						Node node = GraphicalDataHelper.findNode(getDiagram(),
									taskDescriptor, TaskNode.class);
						if (node == null) {
							node = ModelFactory.eINSTANCE.createTaskNode();
							node.setDiagram(getDiagram());
							node.setObject(taskDescriptor);
							((NamedNodeImpl) node).setItemProvider(allElements);
						}
						taskNodes.add(node);
						}
					}
				}
				if(!taskNodes.isEmpty()) {
					// add role node
					//
					Node node = (NamedNodeImpl) ModelFactory.eINSTANCE.createRoleNode();
					node.setDiagram(getDiagram());
					node.setObject(roleDescriptor);
					((NamedNodeImpl) node).setItemProvider(allElements);
					getNodes().add(node);
					// add task nodes
					//
					getNodes().addAll(taskNodes);
				}
			}
		}

		
	}

	private class RoleDescriptorAdapter extends AdapterImpl implements
			INodeChangeListener {
		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
		 */
		public void notifyChanged(Notification msg) {
			if(msg instanceof OppositeFeatureNotification) {
				Object feature = ((OppositeFeatureNotification)msg).getOppsiteFeature();
				if(feature == AssociationHelper.RoleDescriptor_PrimaryTaskDescriptors) {
					switch (msg.getEventType()) {
					case Notification.ADD:
						addNode((TaskDescriptor) msg.getNewValue());
						return;
					case Notification.REMOVE:
						removeNode(msg.getOldValue());
						return;
					case Notification.ADD_MANY:
						addNode((Collection) msg.getNewValue());
						return;
					case Notification.REMOVE_MANY:
						removeNode((Collection) msg.getOldValue());
						return;
					}
				}
			}
			switch (msg.getFeatureID(Activity.class)) {
			case UmaPackage.ACTIVITY__BREAKDOWN_ELEMENTS:
				switch (msg.getEventType()) {
				case Notification.MOVE:
					System.out.println("MOVEEEEEEED");
					break;
				}
				break;
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.epf.diagram.model.util.INodeChangeListener#getNode()
		 */
		public Node getNode() {
			return RoleTaskCompositeImpl.this;
		}

	}

	/**
	 * @param oldValue
	 */
	private void removeNode(Object oldValue) {
		Collection removedNodes = new HashSet();
		for (Iterator iter = nodes.iterator(); iter.hasNext();) {
			Node node = (Node) iter.next();
			if (oldValue instanceof TaskDescriptor) {
				if (node instanceof RoleNode) {
					List list = AssociationHelper.getPrimaryTaskDescriptors((RoleDescriptor) node.getObject());
					if (list == null || list.size() < 1) {
						iter.remove();
						removedNodes.add(node);
					}
				}
			}
			if (node.getObject() == oldValue) {
				iter.remove();
				removedNodes.add(node);
				if (oldValue instanceof TaskDescriptor) {
					Node wpnode = GraphicalDataHelper.findNode(
							(ActivityDetailDiagram) getDiagram(), oldValue);
					getDiagram().getNodes().remove(wpnode);
					wpnode = GraphicalDataHelper.findNode(
							(ActivityDetailDiagram) getDiagram(), oldValue);
					getDiagram().getNodes().remove(wpnode);
				}
			}
		}
		if (nodes.isEmpty()) {
			getDiagram().getNodes().remove(this);
		}
		// pushBackToDiagram(removedNodes);
	}

	/**
	 * @param descriptor
	 */
	private void addNode(TaskDescriptor descriptor) {
		addNode(getNodes(), descriptor);
	}

	private void addNode(Collection nodes, TaskDescriptor descriptor) {

		ActivityDetailDiagramImpl diagram = (ActivityDetailDiagramImpl) getDiagram();
		Node node = GraphicalDataHelper.findNode((NodeContainer) diagram,
				descriptor);
		if (node == null) {
			if (descriptor instanceof TaskDescriptor) {
				RoleDescriptor roleDescriptor = ((TaskDescriptor) descriptor)
						.getPerformedPrimarilyBy();
				Node roleNode = ((ActivityDetailDiagramImpl) getDiagram()).findNode(
						(ActivityDetailDiagram) getDiagram(), roleDescriptor);
				if (roleNode == null) {
					roleNode = ((ActivityDetailDiagramImpl) getDiagram())
							.createRoleTaskComposite(roleDescriptor);
					getDiagram().getNodes().add(roleNode);
				} else {
					node = diagram.createNode(descriptor);
					int i = getNextIndex(getNodes());
					if(node instanceof TaskNode){
						GraphicalDataHelper.createProperty(node.getGraphNode(), 
							GraphicalDataHelper.PROP_INDEX, new Integer(i+1).toString());
					}
					//createTaskInputOutputNodes(descriptor, getDiagram().getNodes());
				}
			}
		}
		if(node != null){
			nodes.add(node);
		}
	}

	/**
	 * @param oldValue
	 */
	private void removeNode(Collection elements) {
		Collection nodes = new ArrayList();
		for (Iterator iter = nodes.iterator(); iter.hasNext();) {
			Node node = (Node) iter.next();
			if (elements.contains(node.getObject())) {
				nodes.add(node);
			}
		}
		getNodes().removeAll(nodes);
		// pushBackToDiagram(nodes);
	}

	/**
	 * @param newValue
	 */
	private void addNode(Collection elements) {
		Collection nodes = new ArrayList();
		for (Iterator iter = elements.iterator(); iter.hasNext();) {
			TaskDescriptor descriptor = (TaskDescriptor) iter.next();
			addNode(nodes, descriptor);
		}

		getNodes().addAll(nodes);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.diagram.model.impl.NodeImpl#getMethodElementAdapterType()
	 */
	public Class getMethodElementAdapterType() {
		return RoleDescriptorAdapter.class;
	}
	
	/**
	 * @return
	 */
	private int getNextIndex(List nodes) {
		int index = 0;
		for(Iterator iterator = nodes.iterator(); iterator.hasNext();){
			Object obj = iterator.next();
			if(obj instanceof Node){
				Node node = (Node)obj;
				//String temp = GraphicalDataHelper.findProperty(node.getGraphNode(), GraphicalDataHelper.PROP_INDEX).getValue();
				List list = node.getGraphNode().getList(UmaPackage.GRAPH_NODE__PROPERTY);
				if(list != null && !list.isEmpty()){
					for (int i = list.size() - 1; i > -1; i--) {
						Property prop = (Property) list.get(i);
						if (GraphicalDataHelper.PROP_INDEX.equals(prop.getKey())){
							int temp  = Integer.parseInt(prop.getValue());
							if(temp > index ) index = temp;
						}
					}
				}
			}
		}
		return index;
	}
	
	private static final Comparator comparator = new Comparator() {

		public int compare(Object obj1, Object obj2) {
			if (obj1 == obj2)
				return 0;
			if(obj1 instanceof Node && obj2 instanceof Node){
				Node node1 = (Node)obj1;
				Node node2 = (Node)obj2;
				if(getIndex(node1) >  getIndex(node2))
					return 1;
			}
			return 0;
		}

	};
	/**
	 * 
	 */
	private static int getIndex(Node node) {
		String temp = getProperty(node, GraphicalDataHelper.PROP_INDEX)
				.getValue();
		int i = Integer.parseInt(temp);
		return i;
	}
	
	private static Property getProperty(Node node, String key) {
		GraphNode graphNode = node.getGraphNode();
		if (graphNode != null) {
			List list = graphNode.getList(UmaPackage.GRAPH_NODE__PROPERTY);
			if (list != null && !list.isEmpty()) {
				for (int i = list.size() - 1; i > -1; i--) {
					Property prop = (Property) list.get(i);
					if (key.equals(prop.getKey())) {
						return prop;
					}
				}
			}
		}
		return null;
	}
	public void createTaskInputOutputNodes(TaskDescriptor descriptor, Collection nodes) {
		Node newNode = null;
		if (!descriptor.getMandatoryInput().isEmpty()) {
			newNode = createWorkProductComposite(descriptor,
					WorkProductComposite.INPUTS);
			nodes.add(newNode);
		}
		if (!descriptor.getOutput().isEmpty()) {
			newNode = createWorkProductComposite(descriptor,
					WorkProductComposite.OUTPUTS);
			nodes.add(newNode);
		}
	}
	
	public WorkProductComposite createWorkProductComposite(
			TaskDescriptor taskDescriptor, int type) {
		WorkProductComposite workproductComposite = ModelFactory.eINSTANCE
				.createWorkProductComposite();
		workproductComposite.setUMAContainer(getGraphNode());
		workproductComposite.setDiagram(getDiagram());
		workproductComposite.setType(type);
		workproductComposite.setObject(taskDescriptor);
		return workproductComposite;
	}
	
} // RoleTaskCompositeImpl
