//------------------------------------------------------------------------------
// 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.authoring.gef.edit;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.BendpointConnectionRouter;
import org.eclipse.draw2d.ConnectionLayer;
import org.eclipse.draw2d.FanRouter;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.epf.authoring.gef.commands.DeleteCommand;
import org.eclipse.epf.authoring.gef.edit.policies.NodeContainerEditPolicy;
import org.eclipse.epf.authoring.gef.edit.policies.NodeContainerFlowLayoutEditPolicy;
import org.eclipse.epf.diagram.model.Diagram;
import org.eclipse.epf.diagram.model.ModelPackage;
import org.eclipse.epf.diagram.model.NamedNode;
import org.eclipse.epf.diagram.model.Node;
import org.eclipse.epf.diagram.model.NodeContainer;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.ComponentEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.gef.ui.actions.ActionRegistry;

/**
 * EditPart for NodeContainer
 * 
 * @author Phong Nguyen Le
 * @since 1.0
 */
public abstract class NodeContainerEditPart extends AbstractGraphicalEditPart
		implements LayerConstants {

	private BendpointConnectionRouter cRouter;

	protected Adapter modelListener = new AdapterImpl() {
		public void notifyChanged(Notification msg) {
			handlePropertyChanged(msg);
		}
	};

	public NodeContainerEditPart(NodeContainer model) {
		super();
		setModel(model);
	}

	public void activate() {
		super.activate();
		NodeContainer model = (NodeContainer) ((EObject) getModel());
		model.eAdapters().add(modelListener);
		model.addConsumer(this);
	}

	protected void createEditPolicies() {
		installEditPolicy(EditPolicy.COMPONENT_ROLE, new ComponentEditPolicy() {
			protected Command createDeleteCommand(GroupRequest deleteRequest) {
				Boolean bool = (Boolean) deleteRequest.getExtendedData().get(
						DeleteCommand.KEY_PERM_DELETE);
				boolean permDelete = bool == null ? false : bool.booleanValue();
				DeleteCommand cmd = new DeleteCommand(permDelete);
				cmd.setPartToBeDeleted(getHost().getModel());
				return cmd;
			}
		});
		installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, null);
		installEditPolicy(EditPolicy.CONTAINER_ROLE,
				new NodeContainerEditPolicy());
		installEditPolicy(EditPolicy.NODE_ROLE, null);
		installEditPolicy(EditPolicy.LAYOUT_ROLE,
				new NodeContainerFlowLayoutEditPolicy());
	}

	public void deactivate() {
		NodeContainer model = (NodeContainer) ((EObject) getModel());
		model.eAdapters().remove(modelListener);
		model.removeConsumer(this);

		super.deactivate();
	}

	protected EditPart findChildByModel(Object model) {
		for (Iterator iter = getChildren().iterator(); iter.hasNext();) {
			EditPart child = (EditPart) iter.next();
			if (child.getModel() == model) {
				return child;
			}
		}
		return null;
	}

	protected void addPartToEdit(Collection partsToEdit, Object node) {
		if (node instanceof NamedNode) {
			EditPart editPart = findChildByModel(node);
			if (editPart instanceof BaseEditPart) {
				DirectEditManager editManager = ((BaseEditPart) editPart)
						.getDirectEditManager();
				if (editManager instanceof ValidatingDirectEditManager
						&& ((ValidatingDirectEditManager) editManager)
								.validate(((NamedNode) node).getName()) != null) {
					partsToEdit.add(editPart);
				}
			}
		}
	}

	protected void handlePropertyChanged(Notification msg) {
		switch (msg.getFeatureID(NodeContainer.class)) {
		case ModelPackage.NODE__LOCATION:
		case ModelPackage.NODE__WIDTH:
			refreshVisuals();
			break;
		case ModelPackage.NODE__OUTGOING_CONNECTIONS:
			refreshSourceConnections();
			break;
		case ModelPackage.NODE__INCOMING_CONNECTIONS:
			refreshTargetConnections();
			break;
		case ModelPackage.NODE_CONTAINER__NODES:
			refreshChildren();

			List partsToEdit = new ArrayList();
			switch (msg.getEventType()) {
			case Notification.ADD:
				addPartToEdit(partsToEdit, msg.getNewValue());
				break;
			case Notification.ADD_MANY:
				for (Iterator iter = ((Collection) msg.getNewValue())
						.iterator(); iter.hasNext();) {
					addPartToEdit(partsToEdit, iter.next());
				}
				break;
			}

			if (getViewer().getControl().isFocusControl()) {
				for (Iterator iter = partsToEdit.iterator(); iter.hasNext();) {
					BaseEditPart editPart = (BaseEditPart) iter.next();
					editPart.performDirectEdit();
				}
			}

			break;
		}
	}

	protected void refreshVisuals() {
		Node node = (Node) getModel();
		Dimension size = new Dimension(node.getWidth(), node.getHeight());
		if (size == null) {
			size = new Dimension(60, 100);
		}
		Point loc = node.getLocation();
		if (loc == null) {
			loc = new Point(10, 10);
		}
		Rectangle r = new Rectangle(loc, size);

		((GraphicalEditPart) getParent()).setLayoutConstraint(this,
				getFigure(), r);

		if (cRouter == null) {
			ConnectionLayer cLayer = (ConnectionLayer) getLayer(CONNECTION_LAYER);
			FanRouter router = new FanRouter();
			router.setSeparation(30);
			cRouter = new BendpointConnectionRouter();
			router.setNextRouter(cRouter);
			cLayer.setConnectionRouter(router);
		}
	}

	protected List getModelChildren() {
		return ((NodeContainer) getModel()).getNodes();
	}
	
	/**
	 * Only useful in case of ADD. Placed here in order to invoke in sub classes.
	 * TODO:  Move this method to utility class.
	 */
	protected void cleanUpDiagram(EditPart part) {
		EditPart dep = (EditPart) part.getParent();
		if(dep instanceof ActivityDetailDiagramEditPart ){
			
			ActivityDetailDiagramEditPart add = ((ActivityDetailDiagramEditPart)dep);
			if(!((Diagram)add.getModel()).isReadOnly()){
				add.getRecentlyAddedParts().addAll(dep.getChildren());
		
				// clean up the diagram
				DefaultEditDomain editingDomain = new DefaultEditDomain(null);
				ActionRegistry actionRegistry = new ActionRegistry();
		
				DiagramActionService actionService = new DiagramActionService(
						(GraphicalViewer)getParent().getViewer(), editingDomain, actionRegistry);
				actionService.registerVerticalAlignFirstSelectedAction();
		
				DiagramUpdateService service = new DiagramUpdateService(
						(GraphicalViewer)getParent().getViewer(), editingDomain, actionRegistry);
				service.cleanUpDiagram();
			}
		}
	}

}