//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010 huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/god4j
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : god4j@lists.sourceforge.net 
 */
//----------------------------END  LICENSE-----------------------------
package org.huihoo.god4j.jgraph;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import org.huihoo.god4j.GOD4JConstants;
import org.huihoo.god4j.GOD4JFactory;
import org.huihoo.god4j.util.BarFactory;
import org.huihoo.god4j.util.StringManager;
import org.huihoo.god4j.util.UUID;
import org.huihoo.workflow.impl.xpdl.WorkflowTransitionImpl;
import org.huihoo.workflow.xpdl.WorkflowActivity;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.jgraph.graph.BasicMarqueeHandler;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.DefaultPort;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.Port;
import org.jgraph.graph.PortView;
public class XPDLJGraphMarqueeHandler extends BasicMarqueeHandler
{
	private static StringManager sm_jgraph = StringManager.getManager(JGraphConstants.PACKAGE);
	private static StringManager sm_god4j = StringManager.getManager(GOD4JConstants.PACKAGE);

	protected XPDLJGraph graph;

	/** Starting point (where mouse was pressed). */
	protected Point2D start;

	/** Current point (where mouse was dragged). */
	protected Point2D current;

	/** Current curPort (when mouse was pressed). */
	protected PortView curPort;

	/** First curPort (when mouse was pressed). */
	protected PortView firstPort;

	/** A point where popup window has been created last time. */
	protected Point2D popupPoint;

	public XPDLJGraphMarqueeHandler(XPDLJGraph argGraph)
	{
		graph = argGraph;
	}

	/** Return true if this handler should be preferred over other handlers. */
	public boolean isForceMarqueeEvent(MouseEvent e)
	{
		if (e.isShiftDown() || e.isControlDown())
		{
			return false;
		}

		if (SwingUtilities.isRightMouseButton(e))
		{
			return true;
		}

		curPort = getSourcePortAt(e.getPoint());
		if (curPort != null && graph.isPortsVisible())
		{
			return true;
		}

		return super.isForceMarqueeEvent(e);
	}

	//-----------------------------------------------------------------------------------
	// Mouse Event Handler
	//-----------------------------------------------------------------------------------	
	public void mousePressed(final MouseEvent event)
	{
		if (SwingUtilities.isRightMouseButton(event))
		{
			Point2D loc = graph.fromScreen(event.getPoint());
			Object cell = graph.getFirstCellForLocation(loc.getX(), loc.getY());
			popupPoint = graph.fromScreen(event.getPoint());
			JPopupMenu menu = createPopupMenu(cell);
			if (menu != null)
			{
				menu.show(graph, event.getX(), event.getY());
			}

			super.mousePressed(event);
		}
		else if (!event.isConsumed() && curPort != null && graph.isPortsVisible())
		{
			start = graph.toScreen(curPort.getLocation(null));
			firstPort = curPort;
			double scale = graph.getScale();
			start = firstPort.getLocation(null);
			start = new Point((int) (start.getX() * scale), (int) (start.getY() * scale));
			if (acceptsSource(firstPort, null))
			{
				event.consume();
				return;
			}

			firstPort = curPort = null;
			start = current = null;
		}
	}
	public void mouseReleased(MouseEvent event)
	{
		if (event != null && !event.isConsumed() && !SwingUtilities.isRightMouseButton(event))
		{
			if (acceptsTarget(curPort,firstPort) && curPort != firstPort)
			{
				connect((Port) firstPort.getCell(), (Port) curPort.getCell());
			}
			else
			{
				Graphics g = graph.getGraphics();
				Color bg = graph.getBackground();
				Color fg = Color.blue;
				g.setColor(fg);
				g.setXORMode(bg);
				curPort = firstPort;
				overlay(g);
			}
			event.consume();
		}
		else
		{
			graph.repaint();
		}

		firstPort = curPort = null;
		start = current = null;
		super.mouseReleased(event);
	}
	public void mouseMoved(MouseEvent e)
	{
		if (e != null && getSourcePortAt(e.getPoint()) != null && graph.isPortsVisible())
		{
			graph.setCursor(new Cursor(Cursor.HAND_CURSOR));
			e.consume();
		}
		else
		{
			super.mouseMoved(e);
		}
	}
	public void mouseDragged(MouseEvent e)
	{
		if (start != null)
		{
			Graphics g = graph.getGraphics();
			PortView newPort = getTargetPortAt(e.getPoint());
			if (newPort == null || newPort != curPort)
			{
				paintConnector(Color.black, graph.getBackground(), g);
				curPort = newPort;
				if (curPort != null)
				{
					current = graph.toScreen(curPort.getLocation(null));
				}
				else
				{
					current = graph.snap(e.getPoint());
				}
				paintConnector(graph.getBackground(), Color.black, g);
			}
		}
		super.mouseDragged(e);
	}
	public PortView getSourcePortAt(Point point)
	{
		graph.setJumpToDefaultPort(false);
		PortView result;
		try
		{
			result = graph.getPortViewAt(point.getX(), point.getY());
		}
		finally
		{
			graph.setJumpToDefaultPort(true);
		}
		return result;
	}
	protected PortView getTargetPortAt(Point point)
	{
		return graph.getPortViewAt(point.getX(), point.getY());
	}
	protected void paintConnector(Color fg, Color bg, Graphics g)
	{
		g.setColor(fg);
		g.setXORMode(bg);
		paintPort(graph.getGraphics());
		if (firstPort != null && start != null && current != null)
		{
			g.drawLine((int) start.getX(), (int) start.getY(), (int) current.getX(), (int) current.getY());
		}
	}
	protected void paintPort(Graphics g)
	{
		if (curPort != null)
		{
			boolean o = (GraphConstants.getOffset(curPort.getAttributes()) != null);
			Rectangle2D r = (o) ? curPort.getBounds() : curPort.getParentView().getBounds();
			r = graph.toScreen((Rectangle2D) r.clone());
			r.setFrame(r.getX() - 3, r.getY() - 3, r.getWidth() + 6, r.getHeight() + 6);
			graph.getUI().paintCell(g, curPort, r, true);
		}
	}
	protected void connect(Port source, Port target)
	{
		DefaultPort sourcePort = (DefaultPort) source;
		DefaultPort targetPort = (DefaultPort) target;
		DefaultGraphCell sourceCell = (DefaultGraphCell) sourcePort.getParent();
		DefaultGraphCell targetCell = (DefaultGraphCell) targetPort.getParent();

		if (sourceCell instanceof BeginActivity)
		{
			WorkflowActivity sourceActivity = (WorkflowActivity) ((BeginActivity) sourceCell).getUserObject();
			WorkflowActivity activity = (WorkflowActivity) targetCell.getUserObject();
			WorkflowTransition trans =
				new WorkflowTransitionImpl(UUID.generate(), sm_jgraph.getString("jgraph.beginTransitionLabel"));
			trans.setWorkflowProcess(graph.getWorkflowProcess());
			trans.setFromWorkflowActivity(sourceActivity);
			trans.setToWorkflowActivity(activity);
			trans.setDescription("");
			graph.connect(source, target, trans, false);
		}
		else if (targetCell instanceof EndActivity)
		{
			WorkflowActivity activity = (WorkflowActivity) sourceCell.getUserObject();
			WorkflowActivity targetActivity = (WorkflowActivity) targetCell.getUserObject();
			WorkflowTransition trans =
				new WorkflowTransitionImpl(UUID.generate(), sm_jgraph.getString("jgraph.endTransitionLabel"));
			trans.setWorkflowProcess(graph.getWorkflowProcess());
			trans.setFromWorkflowActivity(activity);
			trans.setToWorkflowActivity(targetActivity);
			graph.connect(source, target, trans, false);
		}
		else
		{
			WorkflowActivity sourceActivity = (WorkflowActivity) sourceCell.getUserObject();
			WorkflowActivity targetActivity = (WorkflowActivity) targetCell.getUserObject();
			WorkflowTransition trans =
				new WorkflowTransitionImpl(
					UUID.generate(),
					sm_jgraph.getString("jgraph.transitionLabel")
						+ "_"
						+ sourceActivity.getName()
						+ "_to_"
						+ targetActivity.getName());
			trans.setWorkflowProcess(graph.getWorkflowProcess());
			trans.setFromWorkflowActivity(sourceActivity);
			trans.setToWorkflowActivity(targetActivity);
			graph.connect(source, target, trans, false);
		}
	}

	protected JPopupMenu createPopupMenu(final Object cell)
	{
		JPopupMenu menu = new JPopupMenu();
		menu.add(BarFactory.createMenuItem(sm_god4j, "editProperites"));

		if ((cell == null) || (cell instanceof BusinessActivity))
		{
			menu.add(BarFactory.createMenuItem(sm_god4j, "cut"));
			menu.add(BarFactory.createMenuItem(sm_god4j, "copy"));
			menu.add(BarFactory.createMenuItem(sm_god4j, "pasteAt"));
		}

		if (!graph.isSelectionEmpty() && cell != null)
		{
			menu.add(BarFactory.createMenuItem(sm_god4j, "delete"));
			menu.addSeparator();
			menu.add(BarFactory.createMenuItem(sm_god4j, "editProperties"));
		}
		return menu;
	}

	/**
	* Gets the point of last popup menu creation.
	*/
	public Point2D getPopupPoint()
	{
		return popupPoint;
	}

	protected boolean acceptsSource(PortView port, PortView firstPort)
	{
		//	if port is a valid
		if (port != null && port.getCell() != null && (port.getCell() instanceof DefaultPort))
		{
			// and it's parent is instance of Linkable
			// return if it accepts to be a source or a target
			Object cell = ((DefaultPort) port.getCell()).getParent();
			if ((cell instanceof XPDLLinkable))
			{
					if (!((XPDLLinkable) cell).acceptsSource())
					{
						return false;
					}

					return true;
			}
		}
		// return false otherwise
		return false;
	}
	
  protected boolean acceptsTarget(PortView port, PortView firstPort)
	{
		//	if port is a valid
		if (port != null && port.getCell() != null && (port.getCell() instanceof DefaultPort))
		{
			// and it's parent is instance of Linkable
			// return if it accepts to be a source or a target
			Object cell = ((DefaultPort) port.getCell()).getParent();
			if ((cell instanceof XPDLLinkable))
			{
					if (!((XPDLLinkable) cell).acceptsTarget())
					{
						return false;
					}
					Object srcCell = ((DefaultPort) firstPort.getCell()).getParent();
					// do not allow start-end connection
					if ((cell instanceof EndActivity) && (srcCell instanceof BeginActivity))
					{
						JOptionPane.showMessageDialog(
							GOD4JFactory.getGOD4J(),
							sm_jgraph.getString("joptionpane.warnCannotConnectStartAndEnd"),
							sm_jgraph.getString("joptionpane.warn"),
							JOptionPane.WARNING_MESSAGE);
						return false;
					}
					
					if (cell instanceof JGraphActivity)
					{
						JGraphActivity act = (JGraphActivity) cell;
						List trns = act.getIncomingTransitions();
						
						// Do not allow activity to have more than one transition
						// from the same activity
						if (trns.size() > 0)
						{
							Iterator it = trns.iterator();
							while (it.hasNext())
							{
								Object src = ((JGraphTransition) it.next()).getSourceActivity();
								if (src.equals(srcCell))
								{

									JOptionPane.showMessageDialog(
										GOD4JFactory.getGOD4J(),
										sm_jgraph.getString("joptionpane.warnActivityCannotHaveMoreThenOneIncomingOutgoingTransitionFromToTheSameActivity"),
										sm_jgraph.getString("joptionpane.warn"),
										JOptionPane.WARNING_MESSAGE);
									return false;
								}
							}
						}
					return true;
				}
			}
		}
		// return false otherwise
		return false;
	}
}
