//------------------------------------------------------------------------------
// 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.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.impl.ENotificationImpl;
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.WorkProductComposite;
import org.eclipse.epf.diagram.model.util.GraphicalDataHelper;
import org.eclipse.epf.diagram.model.util.INodeChangeListener;
import org.eclipse.epf.library.edit.IFilter;
import org.eclipse.epf.library.edit.TngAdapterFactory;
import org.eclipse.epf.library.edit.util.ConfigurableComposedAdapterFactory;
import org.eclipse.epf.uma.Diagram;
import org.eclipse.epf.uma.GraphNode;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.Property;
import org.eclipse.epf.uma.TaskDescriptor;
import org.eclipse.epf.uma.UMASemanticModelBridge;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.WorkProductDescriptor;


/**
 * <!-- begin-user-doc --> An implementation of the model object '<em><b>Work Product Composite</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 * <li>{@link org.eclipse.epf.diagram.model.impl.WorkProductCompositeImpl#getType <em>Type</em>}</li>
 * </ul>
 * </p>
 * 
 * @generated
 */
public class WorkProductCompositeImpl extends NodeContainerImpl implements
		WorkProductComposite {
	/**
	 * The default value of the '{@link #getType() <em>Type</em>}' attribute.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @see #getType()
	 * @generated
	 * @ordered
	 */
	protected static final int TYPE_EDEFAULT = 0;

	/**
	 * The cached value of the '{@link #getType() <em>Type</em>}' attribute.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @see #getType()
	 * @generated
	 * @ordered
	 */
	protected int type = TYPE_EDEFAULT;

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	protected WorkProductCompositeImpl() {
		super();
		methodElementAdapter = new TaskDescriptorAdapter();
	}

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

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public int getType() {
		return type;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public void setType(int newType) {
		int oldType = type;
		type = newType;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET,
					ModelPackage.WORK_PRODUCT_COMPOSITE__TYPE, oldType, type));
	}

	/**
	 * <!-- 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.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
				return ((InternalEList) getIncomingConnections()).basicAdd(
						otherEnd, msgs);
			case ModelPackage.WORK_PRODUCT_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.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
				return ((InternalEList) getIncomingConnections()).basicRemove(
						otherEnd, msgs);
			case ModelPackage.WORK_PRODUCT_COMPOSITE__OUTGOING_CONNECTIONS:
				return ((InternalEList) getOutgoingConnections()).basicRemove(
						otherEnd, msgs);
			case ModelPackage.WORK_PRODUCT_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.WORK_PRODUCT_COMPOSITE__OBJECT:
			return getObject();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__LOCATION:
			return getLocation();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__WIDTH:
			return new Integer(getWidth());
		case ModelPackage.WORK_PRODUCT_COMPOSITE__HEIGHT:
			return new Integer(getHeight());
		case ModelPackage.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
			return getIncomingConnections();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OUTGOING_CONNECTIONS:
			return getOutgoingConnections();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__READ_ONLY:
			return isReadOnly() ? Boolean.TRUE : Boolean.FALSE;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__NODES:
			return getNodes();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__TYPE:
			return new Integer(getType());
		}
		return eDynamicGet(eFeature, resolve);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public void eSet(EStructuralFeature eFeature, Object newValue) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OBJECT:
			setObject((Object) newValue);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__LOCATION:
			setLocation((Point) newValue);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__WIDTH:
			setWidth(((Integer) newValue).intValue());
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__HEIGHT:
			setHeight(((Integer) newValue).intValue());
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
			getIncomingConnections().clear();
			getIncomingConnections().addAll((Collection) newValue);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OUTGOING_CONNECTIONS:
			getOutgoingConnections().clear();
			getOutgoingConnections().addAll((Collection) newValue);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__READ_ONLY:
			setReadOnly(((Boolean) newValue).booleanValue());
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__NODES:
			getNodes().clear();
			getNodes().addAll((Collection) newValue);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__TYPE:
			setType(((Integer) newValue).intValue());
			return;
		}
		eDynamicSet(eFeature, newValue);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public void eUnset(EStructuralFeature eFeature) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OBJECT:
			setObject(OBJECT_EDEFAULT);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__LOCATION:
			setLocation(LOCATION_EDEFAULT);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__WIDTH:
			setWidth(WIDTH_EDEFAULT);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__HEIGHT:
			setHeight(HEIGHT_EDEFAULT);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
			getIncomingConnections().clear();
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OUTGOING_CONNECTIONS:
			getOutgoingConnections().clear();
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__READ_ONLY:
			setReadOnly(READ_ONLY_EDEFAULT);
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__NODES:
			getNodes().clear();
			return;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__TYPE:
			setType(TYPE_EDEFAULT);
			return;
		}
		eDynamicUnset(eFeature);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public boolean eIsSet(EStructuralFeature eFeature) {
		switch (eDerivedStructuralFeatureID(eFeature)) {
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OBJECT:
			return OBJECT_EDEFAULT == null ? object != null : !OBJECT_EDEFAULT
					.equals(object);
		case ModelPackage.WORK_PRODUCT_COMPOSITE__LOCATION:
			return LOCATION_EDEFAULT == null ? location != null
					: !LOCATION_EDEFAULT.equals(location);
		case ModelPackage.WORK_PRODUCT_COMPOSITE__WIDTH:
			return width != WIDTH_EDEFAULT;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__HEIGHT:
			return height != HEIGHT_EDEFAULT;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__INCOMING_CONNECTIONS:
			return incomingConnections != null
					&& !incomingConnections.isEmpty();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__OUTGOING_CONNECTIONS:
			return outgoingConnections != null
					&& !outgoingConnections.isEmpty();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__READ_ONLY:
			return readOnly != READ_ONLY_EDEFAULT;
		case ModelPackage.WORK_PRODUCT_COMPOSITE__NODES:
			return nodes != null && !nodes.isEmpty();
		case ModelPackage.WORK_PRODUCT_COMPOSITE__TYPE:
			return type != TYPE_EDEFAULT;
		}
		return eDynamicIsSet(eFeature);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	public String toString() {
		if (eIsProxy())
			return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (type: "); //$NON-NLS-1$
		result.append(type);
		result.append(')');
		return result.toString();
	}

	private class TaskDescriptorAdapter 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 (!notificationEnabled)
				return;
			notificationEnabled = false;
			try{
			switch (msg.getFeatureID(TaskDescriptor.class)) {
			case UmaPackage.TASK_DESCRIPTOR__OUTPUT:				
				switch (msg.getEventType()) {
				case Notification.ADD:
					Object obj = msg.getNewValue();
					if (obj != null && type == WorkProductComposite.OUTPUTS) {
						addNode(obj);
					} 
					return;
				case Notification.REMOVE:
					obj = msg.getOldValue();
					if (obj != null && type == WorkProductComposite.OUTPUTS) {
						removeNode(obj);
					}
					return;
				}
				break;
			case UmaPackage.TASK_DESCRIPTOR__MANDATORY_INPUT:				
				switch (msg.getEventType()) {
				case Notification.ADD:
					Object obj = msg.getNewValue();
					if (obj != null && type == WorkProductComposite.INPUTS) {
						addNode(obj);
					} 
					return;
				case Notification.REMOVE:
					obj = msg.getOldValue();
					if (obj != null && type == WorkProductComposite.INPUTS) {
						removeNode(obj);
					}
					return;
				}
				break;	
			}
			}finally{
				notificationEnabled = true;
			}
		}

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

	}

	private IFilter getFilter() {
		DiagramImpl diagram = (DiagramImpl) getDiagram();
		if (diagram.filter == null) {
			ConfigurableComposedAdapterFactory adapterFactory = (ConfigurableComposedAdapterFactory) TngAdapterFactory.INSTANCE
					.getPBS_ComposedAdapterFactory();
			return adapterFactory.getFilter();
		} else {
			return diagram.filter;
		}
	}

	public void setObject(Object newObject) {
		if (newObject instanceof TaskDescriptor) {
			List wplist = new ArrayList();
			if (getType() == WorkProductComposite.INPUTS) {
				if (!((TaskDescriptor) newObject).getMandatoryInput().isEmpty())
					wplist.addAll(((TaskDescriptor) newObject)
							.getMandatoryInput());
			} else {
				if (!((TaskDescriptor) newObject).getOutput().isEmpty())
					wplist.addAll(((TaskDescriptor) newObject).getOutput());
			}

			IFilter filter = getFilter();
			Collection allElements = ((ActivityDetailDiagramImpl) getDiagram())
					.getAllBreakdownElements();
			for (Iterator iter = wplist.iterator(); iter.hasNext();) {
				WorkProductDescriptor wpDescriptor = (WorkProductDescriptor) iter
						.next();
				if (filter == null || filter.accept(wpDescriptor)) {
					Node node = GraphicalDataHelper.findNode(getDiagram(),
							wpDescriptor);
					if (node == null) {
						node = ModelFactory.eINSTANCE
								.createWorkProductDescriptorNode();
						node.setObject(wpDescriptor);
						((NamedNodeImpl) node).setItemProvider(allElements);
					}
					getNodes().add(node);
				}
			}
		}
		super.setObject(newObject);

		if (newObject == null)
			return;

		MethodElement element = (MethodElement) newObject;
		graphNode = getGraphNode(element);

		// System.out.println("NodeImpl.setObject(): element: '" +
		// element.getName() + "', graphNode: " + graphNode);

		// populate/synch data
		//
		if (height == HEIGHT_EDEFAULT) {
			setHeight(graphNode.getSize().getHeight().intValue());
		} else {
			graphNode.getSize().setHeight(new Double(height));
		}

		if (width == WIDTH_EDEFAULT) {
			setWidth(graphNode.getSize().getWidth().intValue());
		} else {
			graphNode.getSize().setWidth(new Double(width));
		}

		if (location == LOCATION_EDEFAULT) {
			setLocation(GraphicalDataHelper.toPoint(graphNode.getPosition()));
		} else {
			graphNode.getPosition().setX(new Double(location.x));
			graphNode.getPosition().setY(new Double(location.y));
		}

		if (type == TYPE_EDEFAULT) {
			List list = graphNode.getList(UmaPackage.GRAPH_NODE__PROPERTY);
			Property property = getPropertyByKey(list,
					GraphicalDataHelper.PROP_WORK_PRODUCT_COMPOSITE_TYPE);
			if (property != null) {
				property.setValue(property.getValue());
				list.add(property);
			}
		} else {
			List list = graphNode.getList(UmaPackage.GRAPH_NODE__PROPERTY);
			Property property = getPropertyByKey(list,
					GraphicalDataHelper.PROP_WORK_PRODUCT_COMPOSITE_TYPE);
			if (property != null) {
				property.setValue(new Integer(getType()).toString());
				list.add(property);
			}
		}

		// add methodElementAdapter
		//
		if (methodElementAdapter != null) {
			element.eAdapters().add(methodElementAdapter);
		}

		//        
		this.eAdapters().add(createNodeAdapter());

	}

	public void addNode(Collection collection) {
		Collection nodes = new ArrayList();
		for (Iterator iter = collection.iterator(); iter.hasNext();) {
			WorkProductDescriptor descriptor = (WorkProductDescriptor) iter
					.next();
			addNode(nodes, descriptor);
		}

		getNodes().addAll(nodes);
	}

	public void removeNode(Object oldValue) {
		Collection removedNodes = new HashSet();
		if (oldValue instanceof TaskDescriptor) {
			Node node = findNode((ActivityDetailDiagram) getDiagram(),
					getObject());
			getDiagram().getNodes().remove(node);
		}
		for (Iterator iter = nodes.iterator(); iter.hasNext();) {
			Node node = (Node) iter.next();
			if (node.getObject() == oldValue) {
				iter.remove();
				removedNodes.add(node);
			}
		}
		// pushBackToDiagram(removedNodes);
	}

	public void addNode(Object descriptor) {
		addNode(getNodes(), descriptor);
	}

	private void addNode(Collection nodes, Object descriptor) {
		
		if (descriptor instanceof TaskDescriptor) {
			Node node = findNode((ActivityDetailDiagram) getDiagram(),
					descriptor);
			if (node == null) {
				node = ((ActivityDetailDiagramImpl) getDiagram())
						.createWorkProductComposite(
								(TaskDescriptor) descriptor, this.type);
				getDiagram().getNodes().add(node);
			}
		}
		if(descriptor instanceof WorkProductDescriptor){
			ActivityDetailDiagramImpl diagram = (ActivityDetailDiagramImpl) getDiagram();
			Node node = GraphicalDataHelper.findNode(this ,
					descriptor);
			if (node == null) {
				if (descriptor instanceof WorkProductDescriptor){
					node = diagram.createNode((WorkProductDescriptor) descriptor);
				}
				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);
//	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.diagram.model.impl.NodeImpl#getMethodElementAdapterType()
	 */
	public Class getMethodElementAdapterType() {
		return TaskDescriptorAdapter.class;
	}

	public Point getLocation() {
		if (getType() == WorkProductComposite.INPUTS) {
			return super.getLocation();
		}
		if (getType() == WorkProductComposite.OUTPUTS) {
			return super.getLocation();
		} else {
			return new Point(-1, -1);
		}
	}

	public GraphNode getGraphNode(MethodElement methodElement) {
		if (getUMADiagram() == null) {
			// new MethodElement
			//
			return createGraphNode(methodElement);
		}
		GraphNode node = findGraphNode(getUMADiagram(), methodElement);
		if (node == null) {
			node = createGraphNode(methodElement);
			// createGraphConnectors(node, getUMADiagram());
			getUMADiagram().getContained().add(node);
		}
		return node;
	}

	public GraphNode findGraphNode(Diagram diagram, Object methodElement) {
		for (Iterator iter = diagram.getContained().iterator(); iter.hasNext();) {
			GraphNode element = (GraphNode) iter.next();
			if (element.getSemanticModel() instanceof UMASemanticModelBridge
					&& methodElement == ((UMASemanticModelBridge) element
							.getSemanticModel()).getElement()) {

				List list = element.getList(UmaPackage.GRAPH_NODE__PROPERTY);
				Property property = getPropertyByKey(list,
						GraphicalDataHelper.PROP_WORK_PRODUCT_COMPOSITE_TYPE);
				if (property != null) {
					String typeString = property.getValue();
					if (typeString != null) {
						if (getType() == new Integer(typeString).intValue())
							return element;
					}
				}
			}
		}
		return null;
	}

	private GraphNode createGraphNode(MethodElement element) {
		GraphNode graphNode = UmaFactory.eINSTANCE.createGraphNode();
		UMASemanticModelBridge modelBridge = UmaFactory.eINSTANCE
				.createUMASemanticModelBridge();
		modelBridge.setElement(element);
		graphNode.setSemanticModel(modelBridge);
		org.eclipse.epf.uma.Point point = UmaFactory.eINSTANCE.createPoint();
		point.setX(new Double(-1));
		point.setY(new Double(-1));
		graphNode.setPosition(point);
		org.eclipse.epf.uma.Dimension dim = UmaFactory.eINSTANCE.createDimension();
		dim.setWidth(new Double(-1));
		dim.setHeight(new Double(-1));
		graphNode.setSize(dim);

		// For setting type of WorkProductComposite
		Property property = UmaFactory.eINSTANCE.createProperty();
		property.setKey(GraphicalDataHelper.PROP_WORK_PRODUCT_COMPOSITE_TYPE);
		property.setValue(new Integer(getType()).toString()); //$NON-NLS-1$
		List list = new ArrayList();
		list.add(property);
		graphNode.set(UmaPackage.GRAPH_NODE__PROPERTY, list);
		return graphNode;
	}

	public Property getPropertyByKey(List list, String key) {
		if (!list.isEmpty()) {
			for (Iterator iror = list.iterator(); iror.hasNext();) {
				Property property = (Property) iror.next();
				if (property != null) {
					if (property.getKey().equals(key)) {
						return property;
					} else {
						return null;
					}
				}
			}
		}
		return null;
	}

	public void setProperty(List list, String key, String value) {

		Property property = getPropertyByKey(list, key);
		if (property != null) {
			property.setValue(value);

		} else {
			property = UmaFactory.eINSTANCE.createProperty();
			property.setKey(key);
			property.setValue(value);
		}
		list.add(property);
	}

	public Node findNode(ActivityDetailDiagram diagram, Object object) {
		for (Iterator iter = diagram.getNodes().iterator(); iter.hasNext();) {
			Node element = (Node) iter.next();
			if (object == element.getObject()
					&& element instanceof WorkProductComposite) {
				if (((WorkProductComposite) element).getType() == this.type)
					return element;
			}
		}
		return null;
	}
} // WorkProductCompositeImpl
