//----------------------------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.Point;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.TransferHandler;

import org.huihoo.god4j.GOD4JFactory;
import org.huihoo.workflow.xpdl.WorkflowActivity;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.jgraph.JGraph;
import org.jgraph.graph.ConnectionSet;
import org.jgraph.graph.DefaultEdge;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphTransferable;
import org.jgraph.graph.ParentMap;
import org.jgraph.graph.Port;
import org.jgraph.plaf.basic.BasicGraphUI;
public class XPDLJGraphUI extends BasicGraphUI
{
	protected Point2D insertionPoint = null;
	protected TransferHandler createTransferHandler()
	{
		return new XPDLTransferHandler();
	}

	public void setInsertionPoint(Point2D p)
	{
		insertionPoint = p;
	}
	public Point2D getInsertionPoint()
	{
		return insertionPoint;
	}

	public XPDLJGraphUI()
	{
	}
	//-----------------------------------------------------------------------
	public class XPDLTransferHandler extends TransferHandler
	{
		/* Pointer to the last cells that were cut or pasted. */
		protected Object in, out;

		protected boolean isCut = false;

		/* How many times the last transferable was inserted. */
		protected int inCount = 0;

		public boolean canImport(JComponent comp, DataFlavor[] flavors)
		{
			for (int i = 0; i < flavors.length; i++)
			{
				if (flavors[i] == GraphTransferable.dataFlavor)
				{
					return true;
				}
			}
			return false;
		}

		/* Public entry point to create a Transferable. */
		public Transferable createTransferable()
		{
			return createTransferable(graph);
		}

		protected final Transferable createTransferable(JComponent c)
		{
			if (c instanceof JGraph)
			{
				JGraph graph = (JGraph) c;
				if (!graph.isSelectionEmpty())
				{
					Object[] flat = graph.getDescendants(graph.order(graph.getSelectionCells()));
					ParentMap pm = ParentMap.create(graphModel, flat, false, true);
					ConnectionSet cs = ConnectionSet.create(graphModel, flat, false);
					Map viewAttributes = GraphConstants.createAttributes(flat, graphLayoutCache);
					Rectangle2D bounds = graph.getCellBounds(graph.getSelectionCells());
					out = flat;
					return create(graph, flat, viewAttributes, bounds, cs, pm);
				}
			}
			return null;
		}

		protected GraphTransferable create(
			JGraph graph,
			Object[] cells,
			Map viewAttributes,
			Rectangle2D bounds,
			ConnectionSet cs,
			ParentMap pm)
		{
			return new GraphTransferable(cells, viewAttributes, bounds, cs, pm);
		}

		protected void exportDone(JComponent comp, Transferable data, int action)
		{
			if (comp instanceof JGraph && data instanceof GraphTransferable)
			{
				Object[] cells = ((GraphTransferable) data).getCells();
				JGraph graph = (JGraph) comp;
				Point p = XPDLJGraphUtils.convertToPoint(insertionPoint);
				if (p == null && action == TransferHandler.MOVE)
				{
					removeCells((XPDLJGraph) graph, cells);
				}
				else if (p != null && handle != null)
				{
					int mod = (action == TransferHandler.COPY) ? InputEvent.CTRL_MASK : 0;
					handle.mouseReleased(new MouseEvent(comp, 0, 0, mod, p.x, p.y, 1, false));
				}

				insertionPoint = null;
			}
		}

		public void exportToClipboard(JComponent compo, Clipboard clip, int action)
		{
			isCut = (action == TransferHandler.MOVE);
			super.exportToClipboard(compo, clip, action);
		}

		public int getSourceActions(JComponent c)
		{
			return COPY_OR_MOVE;
		}

		public boolean importData(JComponent comp, Transferable t)
		{
			try
			{
				if (comp instanceof JGraph)
				{
					JGraph graph = (JGraph) comp;
					Object obj = t.getTransferData(GraphTransferable.dataFlavor);
					GraphTransferable gt = (GraphTransferable) obj;
					return handleInsert(graph, gt);
				}
			}
			catch (Exception exception)
			{
				exception.printStackTrace();
			}
			finally
			{
				isCut = false;
			}

			return false;
		}

		protected boolean handleInsert(JGraph graph, GraphTransferable gt)
		{

			// Get Transferred Cells
			Object[] cells = gt.getCells();

			boolean clone = out != cells || !isCut;
			// May be null
			Point2D p = getInsertionPoint();

			// Check if all cells are in the model
			boolean allInModel = true;
			for (int i = 0; i < cells.length && allInModel; i++)
			{
				allInModel = allInModel && graph.getModel().contains(cells[i]);
			}

			// Count repetitive inserts
			if (in == cells)
			{
				inCount++;
			}
			else
			{
				inCount = (allInModel) ? 1 : 0;
			}

			in = cells;

			double insert_dx = inCount * graph.getGridSize();
			double insert_dy = inCount * graph.getGridSize();

			if (insertionPoint != null)
			{
				insert_dx = insertionPoint.getX();
				insert_dy = insertionPoint.getY();
			}

			Rectangle2D bounds = gt.getBounds();
			double gt_dx = bounds.getX();
			double gt_dy = bounds.getY();

			double dx = insert_dx - gt_dx;
			double dy = insert_dy - gt_dy;

			Map nested = gt.getAttributeMap();
			if (cells == out || graph.isDropEnabled())
			{
				insertCells((XPDLJGraph) graph, cells, nested, gt.getConnectionSet(), clone, dx, dy);
			}
			insertionPoint = null;

			return true;
		}

		protected void insertCells(
			XPDLJGraph graph,
			Object[] cells,
			Map viewAttributeMap,
			ConnectionSet cs,
			boolean clone,
			double dx,
			double dy)
		{
			for (int i = 0; i < cells.length; i++)
			{
				if (!(cells[i] instanceof BusinessActivity))
				{
					continue;
				}

				BusinessActivity oldCell = (BusinessActivity) cells[i];
				WorkflowActivity oldActivity = (WorkflowActivity) oldCell.getUserObject();
				WorkflowActivity newActivity = oldActivity;

				if (clone)
				{
					newActivity = XPDLJGraphUtils.cloneActivity(oldActivity);
				}

				newActivity.getWorkflowProcess().addWorkflowActivity(newActivity);
				GOD4JFactory.getGOD4J().getExplorerPanel().getNodeExplorer().addWorkflowActivity(newActivity);
				BusinessActivity newCell = new BusinessActivity(newActivity);
				graph.putIntoCellsMap(newActivity.getUUID(), newCell);

				Map cellMap = new HashMap();
				cellMap.put(oldCell, newCell);
				Map cellArributeMap = GraphConstants.replaceKeys(cellMap, viewAttributeMap);

				newCell.getAttributes().translate(cellArributeMap.values(), dx + 5, dy + 5);
				GraphConstants.setValue((Map) cellArributeMap.get(newCell), newActivity);
				graph.getGraphLayoutCache().insert(new Object[] { newCell }, cellArributeMap, null, null, null);
				graph.updateActivityAttributes(newCell);
			}
			//			}
		}

		protected void removeCells(XPDLJGraph graph, Object[] cells)
		{
			for (int i = 0; i < cells.length; i++)
			{
				if (cells[i] instanceof BusinessActivity)
				{
					BusinessActivity cell = (BusinessActivity) cells[i];
					//删除相应的edge
					ArrayList cellsToRemove = new ArrayList();
					WorkflowActivity activity = (WorkflowActivity) cell.getUserObject();
					graph.getActivityCellsMap().remove(activity.getUUID());
					Port port = cell.getDefaultPort();
					Iterator it = port.edges();
					int n = 0;
					while (it.hasNext())
					{
						DefaultEdge obj = (DefaultEdge) it.next();
						cellsToRemove.add(obj);
						//删除相应的节点
						WorkflowTransition transition = (WorkflowTransition) obj.getUserObject();
						graph.getWorkflowProcess().getWorkflowTransitions().remove(transition);
						ArrayList arr = new ArrayList();
						arr.add(transition);
						GOD4JFactory.getGOD4J().getExplorerPanel().getNodeExplorer().removeWorkflowTransition(transition);
						graph.removeFromCellsMap(transition.getUUID());
						n++;
					}
					cellsToRemove.add(cell);
					cellsToRemove.add(port);
					Object[] remove = cellsToRemove.toArray(new Object[cellsToRemove.size()]);
					graphModel.remove(remove);
					graph.getWorkflowProcess().getWorkflowActivities().remove(activity);
					GOD4JFactory.getGOD4J().getExplorerPanel().getNodeExplorer().removeWorkflowActivity(activity);
					graph.removeFromCellsMap(activity.getUUID());
				}
			}
		}
		// For subclassers if above does not handle the insertion
		protected boolean importDataImpl(JComponent comp, Transferable t)
		{
			return false;
		}
	}
	//-----------------------------------------------------------------------
}