/**
 * Copyright (C) 2009 BonitaSoft S.A.
 * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2.0 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.bonitasoft.studio.diagram.custom.editPolicies;

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

import org.bonitasoft.studio.common.gmf.tools.GMFTools;
import org.bonitasoft.studio.diagram.custom.commands.SwitchPoolOrderCommand;
import org.bonitasoft.studio.diagram.custom.parts.CustomMainProcessEditPart;
import org.bonitasoft.studio.pics.Pics;
import org.bonitasoft.studio.pics.PicsConstants;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.ImageFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.editparts.ZoomListener;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.editpolicies.SelectionEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;

/**
 * @author Aurelien Pupier
 * @author Romain Bioteau - adaptation from AbstractCHangeSpanOnSelectionEditPolicy
 * This class show arrow to swith pool position
 */
public class SwitchPoolSelectionEditPolicy extends SelectionEditPolicy implements ZoomListener{

	public static final String SWITCH_POOL_SELECTION_FEEDBACK_ROLE = "switchPoolselectionFeedback"; //$NON-NLS-1$

	/*The list of figure that catch mouse event to launch the command to change the span*/
	protected List<IFigure> figures = new ArrayList<IFigure>();

	public static final String ADD_LEFT = "addLeft"; //$NON-NLS-1$
	public static final String ADD_RIGHT = "addRight"; //$NON-NLS-1$
	public static final String ADD_TOP = "addTop"; //$NON-NLS-1$
	public static final String ADD_BOTTOM = "addBottom"; //$NON-NLS-1$
	public static final String REMOVE_LEFT = "removeLeft"; //$NON-NLS-1$
	public static final String REMOVE_RIGHT = "removeRight"; //$NON-NLS-1$
	public static final String REMOVE_TOP = "removeTop"; //$NON-NLS-1$
	public static final String REMOVE_BOTTOM = "removeBottom"; //$NON-NLS-1$

	/* The map used to know which place are taken by a widget*/
	protected List<List<Boolean>> map = new ArrayList<List<Boolean>>();

	/* The handler layer on which the figures are draw*/
	protected IFigure layer = null;

	private FigureListener figureListener;

	private IFigure sourceFigure;

	/**
	 * same as focus of SelectionEditPolicy but accessible...
	 */
	private boolean hasFocus;

	private int state;

	private ZoomManager zoomManager;

	/**
	 * 
	 */
	public SwitchPoolSelectionEditPolicy() {
		figureListener = new FigureListener() {

			public void figureMoved(IFigure source) {
				hideSelection();
				if(hasFocus || state == EditPart.SELECTED || state == EditPart.SELECTED_PRIMARY){
					showSelection();
				}
			}
		};
	}

	@Override
	public void activate() {
		super.activate();
		this.zoomManager = ((DiagramRootEditPart) getHost().getRoot()).getZoomManager();
		zoomManager.addZoomListener(this) ;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editpolicies.SelectionEditPolicy#hideSelection()
	 */
	@Override
	protected void hideSelection() {
		if (layer != null) {
			for (IFigure fig : figures) {
				// fig.erase();
				fig.removeFigureListener(figureListener);
				layer.remove(fig);
			}
			figures.clear();
			layer.repaint();
		}
		layer = null;
		map.clear();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editpolicies.SelectionEditPolicy#showSelection()
	 */
	@Override
	protected void showSelection() {
		if(getHost().getParent() instanceof CustomMainProcessEditPart){
			if (sourceFigure == null) {
				sourceFigure = getHostFigure();
				sourceFigure.addFigureListener(figureListener);
			}

			hideSelection();
			layer = getLayer(LayerConstants.HANDLE_LAYER);
			if(layer != null){
				/*
				 * need to flush in order to have the new correct size of the host
				 * figure
				 */
				((IGraphicalEditPart) getHost()).getViewer().flush();
				Rectangle ref = getHostFigure().getBounds();

				if (ref.x == 0 && ref.y == 0 && ref.height == 0 && ref.width == 0) {
					return;
				}

				if(zoomManager.getZoom() > GMFTools.MINIMAL_ZOOM_DISPLAY){
					showSelectionForAddBottom();
					showSelectionForAddTop();
				}
			}
		}
	}

	/**
	 * 
	 */
	private void showSelectionForAddBottom() {
		if (!isOnBottom()) {

			IFigure f = new ImageFigure(Pics.getImage(PicsConstants.arrowDown));
			f.setSize(20, 20);

			sourceFigure = getHostFigure() ;

			Rectangle bounds = sourceFigure.getBounds().getCopy();
			//get the absolute coordinate of bounds
			sourceFigure.translateToAbsolute(bounds);
			IFigure parentFigure = sourceFigure.getParent();
			while( parentFigure != null  ) {
				if(parentFigure instanceof Viewport) {
					Viewport viewport = (Viewport)parentFigure;
					bounds.translate(
							viewport.getHorizontalRangeModel().getValue(),
							viewport.getVerticalRangeModel().getValue());
					parentFigure = parentFigure.getParent();
				}
				else {
					parentFigure = parentFigure.getParent();
				}
			}

			f.setLocation(new Point(bounds.getBottomLeft().x,bounds.getBottom().y));

			f.addMouseListener(new MouseListenerForSpan(AbstractSwitchLaneSelectionEditPolicy.ADD_BOTTOM));
			layer.add(f);
			figures.add(f);
		}
	}

	private boolean isOnBottom() {
		CustomMainProcessEditPart mainProcEp = (CustomMainProcessEditPart) getHost().getParent();
		return mainProcEp.getChildren().indexOf(getHost()) == mainProcEp.getChildren().size()-1 ;
	}

	private boolean isOnTop() {
		CustomMainProcessEditPart mainProcEp = (CustomMainProcessEditPart) getHost().getParent();
		return mainProcEp.getChildren().indexOf(getHost()) == 0;
	}

	/**
	 * 
	 */
	private void showSelectionForAddTop() {
		if (!isOnTop()) {

			IFigure f = new ImageFigure(Pics.getImage(PicsConstants.arrowUp));
			f.setParent(getHostFigure());
			f.setSize(20, 20);



			sourceFigure = getHostFigure() ;

			Rectangle bounds = sourceFigure.getBounds().getCopy();
			//get the absolute coordinate of bounds
			sourceFigure.translateToAbsolute(bounds);
			IFigure parentFigure = sourceFigure.getParent();
			while( parentFigure != null  ) {
				if(parentFigure instanceof Viewport) {
					Viewport viewport = (Viewport)parentFigure;
					bounds.translate(
							viewport.getHorizontalRangeModel().getValue(),
							viewport.getVerticalRangeModel().getValue());
					parentFigure = parentFigure.getParent();
				}
				else {
					parentFigure = parentFigure.getParent();
				}
			}

			f.setLocation(new Point(bounds.getTopLeft().x,bounds.getTop().y-20));
			f.addMouseListener(new MouseListenerForSpan(AbstractSwitchLaneSelectionEditPolicy.ADD_TOP));
			layer.add(f);
			figures.add(f);
		}
	}

	protected void refresh() {
		hideSelection();
		showSelection();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editpolicies.SelectionEditPolicy#deactivate()
	 */
	@Override
	public void deactivate() {
		super.deactivate();
		zoomManager.removeZoomListener(this) ;
		if(sourceFigure != null){
			sourceFigure.removeFigureListener(figureListener);
			sourceFigure = null;
		}
		layer = null;
		figures.clear();	
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editpolicies.SelectionEditPolicy#setFocus(boolean)
	 */
	@Override
	protected void setFocus(boolean value) {
		hasFocus = value;
		super.setFocus(value);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editpolicies.SelectionEditPolicy#setSelectedState(int)
	 */
	@Override
	protected void setSelectedState(int type) {
		state = type;
		super.setSelectedState(type);
	}

	private class MouseListenerForSpan extends MouseListener.Stub{

		private String type;

		/**
		 * 
		 */
		public MouseListenerForSpan(String type) {
			this.type = type;
		}

		public void mousePressed(MouseEvent me) {
			try {
				IUndoableOperation c = new SwitchPoolOrderCommand((IGraphicalEditPart) getHost(), type);
				OperationHistoryFactory.getOperationHistory().execute(c,null,null);
				me.consume();
				refresh();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
	}

	public void zoomChanged(double zoom) {
		hideSelection();
	}
}