/*******************************************************************************
 * Copyright (c) 2005 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 API and implementation
 *******************************************************************************/
package org.eclipse.bpel.ui.editparts;

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

import org.eclipse.bpel.ui.editparts.borders.PickBorder;
import org.eclipse.bpel.ui.editparts.policies.BPELOrderedLayoutEditPolicy;
import org.eclipse.bpel.ui.figures.CenteredConnectionAnchor;
import org.eclipse.bpel.ui.util.ModelHelper;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.FlowLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;

public class IfEditPart extends PickEditPart {

	/**
	 * This is a specical EditPart that represents the If node in the top left
	 * corner of an If activity. It looks like an ElseIfEditPart but has no own
	 * model representation.
	 * 
	 * It displays the activity children of the If activty and layouts together
	 * with the other ElseIfEditParts (representing Else and ElseIf).
	 */
	private class IfNodeEditPart extends ElseIfEditPart{

		/**
		 * This EditPart does not have its own children but displays the
		 * activity children of its parent (the IfEditPart). Therefor we return
		 * the parent's children list without the non-activity elements.
		 */
		@Override
		public List getChildren() {
			List<EditPart> children = new ArrayList<EditPart>();
			for (Iterator i = getParent().getChildren().iterator(); i.hasNext();) {
				EditPart child = (EditPart) i.next();
				if (!(child instanceof ElseIfEditPart)) {
					children.add(child);
				}
			}
			return children;
		}
		
		@Override
		protected void handleModelChanged() {
			/*
			 * don't react on model changes - everything is handled by the
			 * hosting IfEditPart
			 */
			return;
		}

		@Override
		protected void refreshChildren() {
			/*
			 * refreshing children is done by the hosting IfEditPart
			 */
			return;
		}

		@Override
		protected void addChildVisual(EditPart childEditPart, int index) {
			super.addChildVisual(childEditPart, index);
		}
		
		@Override
		protected void removeChildVisual(EditPart childEditPart) {
			super.removeChildVisual(childEditPart);
		}
	}

	/**
	 * Modified layout policy for the IfEditPart
	 */
	private class IfOrderedLayoutEditPolicy extends BPELOrderedLayoutEditPolicy {

		/*
		 * This one is tricky - it works together with the overwritten
		 * getConnectionChildren() method below.
		 * 
		 * We create horizontal connections for the connection children of the
		 * IfEditPart - getConnectionChildren() returns all else and elseIf
		 * children AND the "fake" IfNodeEditPart.
		 * 
		 * We create vertical connections for the connection children of the
		 * IfNodeEditPart - getConnectionChildren() returns all activity
		 * (everything except else and elseIf) of the IfEditPart.
		 */
		@SuppressWarnings("unchecked")
		@Override
		public void refreshConnections() {
			// remove connections before redrawing
			clearConnections();

			if (!isCollapsed()) {
				polyLineConnectionList = createHorizontalConnections((BPELEditPart) getHost());
				polyLineConnectionList
						.addAll(createVerticalConnections(getIfNodeEditPart()));
			}
		}

		/*
		 * The IfNodeEditPart's layout policy is responsible for handling the
		 * target feedbacks.
		 */
		@Override
		protected void eraseLayoutTargetFeedback(Request request) {
			EditPolicy policy = ifEditPart
					.getEditPolicy(EditPolicy.LAYOUT_ROLE);
			policy.eraseTargetFeedback(request);
		}

		/**
		 * Returns list of children to create connections for.
		 */
		@SuppressWarnings("unchecked")
		@Override
		protected List getConnectionChildren(BPELEditPart editPart) {
			List<EditPart> children = getChildren();
			List<EditPart> newChildren = new ArrayList<EditPart>();

			if (editPart instanceof ElseIfEditPart) {
				// For the IfNodeEditPart we return all activity children of the
				// IfEditPart.
				for (EditPart child : children) {
					if (!(child instanceof ElseIfEditPart)) {
						newChildren.add(child);
					}
				}
			} else {
				// For the IfEditPart we return all non-activity children...
				for (EditPart child : children) {
					if (child instanceof ElseIfEditPart) {
						newChildren.add(child);
					}
				}
				// ... and additionaly add our "fake" IfNodeEditPart.
				newChildren.add(getIfNodeEditPart());
			}
			return newChildren;
		}

		/*
		 * The IfNodeEditPart's layout policy is responsible for handling the
		 * target feedbacks.
		 */
		@Override
		public int getFeedbackIndexFor(Request request) {
			EditPolicy policy = ifEditPart
					.getEditPolicy(EditPolicy.LAYOUT_ROLE);
			// TODO: Changed the type to BPELOrderedLayoutEditPolicy?
			return ((BPELOrderedLayoutEditPolicy) policy)
					.getFeedbackIndexFor(request);
			
		}

		/*
		 * The IfNodeEditPart's layout policy is responsible for handling the
		 * target feedbacks.
		 */
		@Override
		protected void showLayoutTargetFeedback(Request request) {
			EditPolicy policy = ifEditPart
					.getEditPolicy(EditPolicy.LAYOUT_ROLE);
			policy.showTargetFeedback(request);
		}
	}
	
	/**
	 * Extending the behaviour for horizontal layout
	 * @author ascharf
	 *
	 */
	private class IfOrderedHorizontalLayoutEditPolicy extends IfEditPart.IfOrderedLayoutEditPolicy{
		@Override
		public void refreshConnections() {
			// remove connections before redrawing
			clearConnections();

			if (!isCollapsed()) {
				polyLineConnectionList = createHorizontalConnections((BPELEditPart) getHost());
				polyLineConnectionList.addAll(createVerticalConnections(getIfNodeEditPart()));
			}
		}
		
		@Override
		protected ArrayList createVerticalConnections(BPELEditPart parent) {
			ArrayList connections = new ArrayList();
			List children = getConnectionChildren(parent);
			BPELEditPart sourcePart, targetPart;
			ConnectionAnchor sourceAnchor = null, targetAnchor = null;
			
			// The real first child is the IfNodeEditPart, so set the first sourceAnchor 
			// to this
			Label nameLabel = ((IfNodeEditPart)parent).nameLabel;
			sourceAnchor = new CenteredConnectionAnchor(nameLabel, CenteredConnectionAnchor.RIGHT,0);
			
			for(int i=0; i < children.size(); i++){
				if(i==0){
					targetPart = (BPELEditPart)children.get(0);
					targetAnchor = targetPart.getConnectionAnchor(CenteredConnectionAnchor.LEFT);
				}
				if(i < children.size()-1){
					sourcePart = (BPELEditPart)children.get(i);
					sourceAnchor = sourcePart.getConnectionAnchor(CenteredConnectionAnchor.RIGHT);
				
					targetPart = (BPELEditPart)children.get(i+1);
					targetAnchor = targetPart.getConnectionAnchor(CenteredConnectionAnchor.LEFT);
				}
				if(sourceAnchor != null && targetAnchor != null)
					connections.add(createConnection(sourceAnchor,targetAnchor,arrowColor));
			}
			
			return connections;
		}
		
		@Override
		protected ArrayList createHorizontalConnections(BPELEditPart parent) {
			ArrayList connections = new ArrayList();
			List children = getConnectionChildren(parent);
			BPELEditPart sourcePart, targetPart;
			ConnectionAnchor sourceAnchor, targetAnchor;
			
			sourcePart = parent;
			sourceAnchor = sourcePart.getConnectionAnchor(CenteredConnectionAnchor.LEFT);
			
			if (children != null){
				for (int i = 0; i < children.size(); i++) {
					targetPart = (BPELEditPart)children.get(i);
					targetAnchor = targetPart.getConnectionAnchor(CenteredConnectionAnchor.LEFT);
					connections.add(createConnection(sourceAnchor,targetAnchor,arrowColor));
				}			
			}		
			return connections;
		}
	}

	private IfNodeEditPart ifEditPart;

	@Override
	public void activate() {
		super.activate();
		getIfNodeEditPart().activate();
	}

	@Override
	public void deactivate() {
		getIfNodeEditPart().deactivate();
		super.deactivate();
	}

	@Override
	public void refreshVisuals() {
		getIfNodeEditPart().refreshVisuals();
		super.refreshVisuals();
	}

	/**
	 * creates the "fake" IfNodeEditPart that is displayed in the top left
	 * corner of the IfEditPart.
	 * 
	 */
	private void createIfNode() {
		this.ifEditPart = new IfNodeEditPart();
		this.ifEditPart.setModel(getModel());
		this.ifEditPart.setParent(this);
		this.ifEditPart.createEditPolicies();

		// we need a special layout policy since this is only a "fake" node
		this.ifEditPart.installEditPolicy(EditPolicy.LAYOUT_ROLE,
				new BPELOrderedLayoutEditPolicy());
	}

	/**
	 * Checks whether the IfNodeEditPart of the current IfEditPart is present
	 * and creates it if not.
	 * 
	 * @return the IfNodeEditPart of the current IfEditPart
	 */
	private IfNodeEditPart getIfNodeEditPart() {
		if (this.ifEditPart == null) {
			createIfNode();
		}
		return this.ifEditPart;
	}

	@Override
	protected void addChildVisual(EditPart childEditPart, int index) {
		if (childEditPart instanceof ElseIfEditPart) {
			// add the "nodes" to the IfEditPart
			super.addChildVisual(childEditPart, -1);
		} else {
			// add everything else to the IfNodeEditPart
			getIfNodeEditPart().addChildVisual(childEditPart, index);
		}
	}

	@Override
	protected void configureExpandedFigure(IFigure figure) {
		super.configureExpandedFigure(figure);

		/*
		 * Add the if node as "fake" visual child. Doning this here ensures the
		 * if node is alway present when the IfEditPart is expanded.
		 */
		this.addChildVisual(getIfNodeEditPart(), 0);
	}

	@Override
	protected void createEditPolicies() {
		super.createEditPolicies();
		if(ModelHelper.isHorizontalLayout(getModel()))
			installEditPolicy(EditPolicy.LAYOUT_ROLE,new IfOrderedHorizontalLayoutEditPolicy());
		else
			installEditPolicy(EditPolicy.LAYOUT_ROLE,new IfOrderedLayoutEditPolicy());
	}

	@Override
	protected void removeChildVisual(EditPart childEditPart) {
		if (childEditPart instanceof ElseIfEditPart) {
			// remove the "nodes" form the IfEditPart
			super.removeChildVisual(childEditPart);
		} else {
			// remove everything else from the IfNodeEditPart
			getIfNodeEditPart().removeChildVisual(childEditPart);
		}
	}

	@Override
	protected void unregisterVisuals() {
		getIfNodeEditPart().unregisterVisuals();
		super.unregisterVisuals();
	}

	/**
	 * Overridden to also switch the layout of our fake node
	 */
	@Override
	public void switchLayout(boolean horizontal) {
		removeEditPolicy(EditPolicy.LAYOUT_ROLE);
		
		EditPolicy newPolicy = null;
		if(horizontal)
			newPolicy = new IfOrderedHorizontalLayoutEditPolicy();
		else
			newPolicy = new IfOrderedLayoutEditPolicy();
		installEditPolicy(EditPolicy.LAYOUT_ROLE, newPolicy);
		
		((FlowLayout)contentFigure.getLayoutManager()).setHorizontal(!horizontal);
		((PickBorder)contentFigure.getBorder()).setHorizontal(horizontal);
		
		getIfNodeEditPart().switchLayout(horizontal);
	}
}
