/*******************************************************************************
 * Copyright (c) 2009 Red Hat, Inc.
 * 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:
 *     Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph.graphlisteners;

import java.util.List;

import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.linuxtools.internal.callgraph.StapGraph;
import org.eclipse.linuxtools.internal.callgraph.StapNode;
import org.eclipse.linuxtools.internal.callgraph.core.FileFinderOpener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.zest.core.widgets.GraphNode;

public class StapGraphMouseListener implements MouseListener {
	private StapGraph graph;
	private StapGraphMouseMoveListener listener;
	private StapGraphMouseExitListener exitListener;

	public StapGraphMouseListener(StapGraph g) {
		this.graph = g;
		listener = new StapGraphMouseMoveListener(graph);
		exitListener = new StapGraphMouseExitListener(listener);
	}

	@Override
	public void mouseDoubleClick(MouseEvent e) {
		if (e.stateMask == SWT.CONTROL) {
			controlDoubleClick();
			return;
		}

		if (graph.getDrawMode() == StapGraph.CONSTANT_DRAWMODE_RADIAL) {
			StapNode node = getNodeFromSelection();
			if (node == null) {
				return;
			}

			graph.getTreeViewer().collapseToLevel(node.getData(), 0);
			graph.getTreeViewer().expandToLevel(node.getData(), 0);
			graph.getTreeViewer().setSelection(new StructuredSelection(node.getData()));

			int id = node.getData().id;

			graph.scale = 1;
			// Redraw in the current mode with the new id as the center
			// The x,y parameters to draw() are irrelevant for radial mode
			graph.draw(id);

			// Unhighlight the center node and give it a normal colour
			node = graph.getNode(id);
			node.unhighlight();
			if (graph.getNodeData(id).isMarked()) {
				node.setBackgroundColor(StapGraph.CONSTANT_MARKED);
			} else {
				node.setBackgroundColor(graph.DEFAULT_NODE_COLOR);
			}
			return;
		} else {
			StapNode node = getNodeFromSelection();
			if (node == null) {
				return;
			}

			unhighlightall(node);
			graph.setSelection(null);

			// Draw in current modes with 'id' at the top
			int id = node.getData().id;
			graph.draw(id);
		}

		graph.setSelection(null);
	}


	@Override
	public void mouseDown(MouseEvent e) {
		if (graph.getProjectionist() != null) {
			graph.getProjectionist().pause();
		}
		mouseDownEvent(e.x, e.y);
	}

	@Override
	public void mouseUp(MouseEvent e) {
		listener.setStop(true);
		graph.removeMouseMoveListener(listener);
		graph.removeListener(SWT.MouseExit, exitListener);

		List<StapNode> list = graph.getSelection();

		if (list.size() == 1) {
			int id;
			if (list.get(0) != null) {
				id = list.get(0).id;
			} else {
				graph.setSelection(null);
				return;
			}
			graph.setSelection(null);

			// ------------Highlighting
			if (graph.getDrawMode() == StapGraph.CONSTANT_DRAWMODE_TREE
					|| graph.getDrawMode() == StapGraph.CONSTANT_DRAWMODE_LEVEL) {
				for (StapNode n : (List<StapNode>) graph.getNodes()) {
					unhighlightall(n);
				}

				List<Integer> callees = null;

				if (graph.isCollapseMode()) {
					callees = graph.getNodeData(id).collapsedChildren;
				} else {
					callees = graph.getNodeData(id).children;
				}

				for (int subID : callees) {
					if (graph.getNode(subID) != null) {
						graph.getNode(subID).highlight();
					}
				}

				if (graph.getParentNode(id) != null) {
					graph.getParentNode(id).highlight();
				}
				graph.getNode(id).highlight();
				return;
			}

		} else if (list.size() == 0 && ! (graph.getDrawMode() == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)) {
			for (StapNode n : (List<StapNode>) graph.getNodes()) {
				unhighlightall(n);
			}

		}
	}

	private void unhighlightall(StapNode n) {
		int id = n.id;
		List<Integer> callees = null;

		if (graph.isCollapseMode()) {
			callees = graph.getNodeData(id).collapsedChildren;
		} else {
			callees = graph.getNodeData(id).children;
		}
		if (callees == null) {
			return;
		}

		for (int subID : callees) {
			if (graph.getNode(subID) != null) {
				graph.getNode(subID).unhighlight();
			}
		}

		if (graph.getParentNode(id) != null) {
			graph.getParentNode(id).unhighlight();
		}
		n.unhighlight();
	}


	private StapNode getNodeFromSelection() {
		List<GraphNode> stapNodeList = graph.getSelection();
		if (stapNodeList.isEmpty() || stapNodeList.size() != 1) {
			graph.setSelection(null);
			return null;
		}

		StapNode node = null;
		if (stapNodeList.get(0) instanceof StapNode) {
			node = (StapNode) stapNodeList.remove(0);
		} else {
			graph.setSelection(null);
			return null;
		}
		return node;
	}

	private GraphNode getAggregateNodeFromSelection() {
		List<GraphNode> graphNodeList = graph.getSelection();
		if (graphNodeList.isEmpty() || graphNodeList.size() != 1) {
			graph.setSelection(null);
			return null;
		}

		GraphNode node = null;
		if (graphNodeList.get(0) != null) {
			node = graphNodeList.remove(0);
		} else {
			graph.setSelection(null);
			return null;
		}
		return node;
	}

	public void controlDoubleClick() {
		if (graph.getDrawMode() == StapGraph.CONSTANT_DRAWMODE_AGGREGATE) {
			GraphNode node = getAggregateNodeFromSelection();

			if (node == null) {
				return;
			}

			String functionName = (String) node.getData("AGGREGATE_NAME"); //$NON-NLS-1$
			FileFinderOpener.findAndOpen(graph.getProject(), functionName);
			node.unhighlight();
		} else {
			StapNode node = getNodeFromSelection();

			if (node == null) {
				return;
			}

			int caller = node.getData().id;

			if (caller < graph.getFirstUsefulNode()) {
				// The only node that satisfies this condition should be main
				caller = graph.getFirstUsefulNode();
			}
			FileFinderOpener.findAndOpen(graph.getProject(), graph.getNodeData(caller).name);
			node.unhighlight();
		}

		graph.setSelection(null);
	}

	public void mouseDownEvent(int x, int y) {
		List<?> list = graph.getSelection();
		if (list.size() < 1) {
			listener.setPoint(x, y);
			listener.setStop(false);
			graph.addMouseMoveListener(listener);
			graph.addListener(SWT.MouseExit, exitListener);
		}
	}

}
