/*******************************************************************************
 * Copyright (c) 2004, 2008 Tasktop Technologies 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:
 *     Tasktop Technologies - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.pde.ui;

import java.util.Locale;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.internal.ide.ui.XmlNodeHelper;
import org.eclipse.mylyn.monitor.ui.AbstractUserInteractionMonitor;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.IPluginObject;
import org.eclipse.pde.internal.core.plugin.ImportObject;
import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
import org.eclipse.pde.internal.core.text.IDocumentElementNode;
import org.eclipse.pde.internal.core.text.plugin.PluginModel;
import org.eclipse.pde.internal.core.text.plugin.PluginObjectNode;
import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor;
import org.eclipse.pde.internal.ui.editor.plugin.ManifestSourcePage;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.FileEditorInput;

/**
 * @author Shawn Minto
 * @author Mik Kersten
 */
@SuppressWarnings("restriction")
public class PdeEditingMonitor extends AbstractUserInteractionMonitor {

	public PdeEditingMonitor() {
		super();
	}

	@Override
	protected void handleWorkbenchPartSelection(IWorkbenchPart part, ISelection selection, boolean contributeToContext) {
		if (part instanceof ManifestEditor) {
			TextSelection textSelection = null;
			IEditorInput in = null;

			// assume that we are editing an xml file due to the editor used
			// this is the plugin.xml editor

			ManifestEditor editor = (ManifestEditor) part;

			// fix bug when user is looking in the cvs repository since the
			// input
			// is not a FileEditorInput
			if (!(editor.getEditorInput() instanceof FileEditorInput)) {
				return;
			}

			// make sure that the selection is a text selection
			if (!(editor.getSelection() instanceof TextSelection || editor.getSelection() instanceof StructuredSelection)) {
				return;
			} else if (editor.getSelection() instanceof StructuredSelection) {
				StructuredSelection s = (StructuredSelection) editor.getSelection();
				if (s.getFirstElement() instanceof PluginObjectNode) {
					PluginObjectNode n = (PluginObjectNode) s.getFirstElement();
					textSelection = new TextSelection(n.getOffset(), n.getLength());
				} else if (s.getFirstElement() instanceof ImportObject) {
					ImportObject io = (ImportObject) s.getFirstElement();
					if (io.getImport() instanceof PluginObjectNode) {
						PluginObjectNode n = (PluginObjectNode) io.getImport();
						textSelection = new TextSelection(n.getOffset(), n.getLength());
					}
				} else {
					return;
				}
			} else {
				// get the selection and the editor input
				textSelection = (TextSelection) editor.getSelection();
			}
			in = editor.getEditorInput();

			// check if we have a text selection
			if (textSelection != null) {
				try {

					// get the node for the selection
					int offset = textSelection.getOffset();
					PluginObjectNode node = getNode(editor, offset, false);

					if (node != null) {
						FileEditorInput fei = (FileEditorInput) in;

						// fix a bug when there is a selection and the editor
						// input is the manifest.mf file
						// not the plugin.xml
						if (fei.getFile().getFullPath().toString().toLowerCase(Locale.ENGLISH).endsWith("/manifest.mf")) { //$NON-NLS-1$
							return;
						}

						String nodeString = getStringOfNode(node);
						if (nodeString == null) {
							return;
						}

						// create the helper to get the handle for the node
						XmlNodeHelper xnode = new XmlNodeHelper(fei.getFile().getFullPath().toString(),
								nodeString.hashCode());

						// get the name for the node
//						String name = node.getXMLAttributeValue("name");
//						if (name == null)
//							name = node.getXMLTagName();
						super.handleElementSelection(part, xnode, contributeToContext);
					}
				} catch (Exception e) {
					StatusHandler.log(new Status(IStatus.ERROR, PdeUiBridgePlugin.ID_PLUGIN,
							"Could not resolve selection", e)); //$NON-NLS-1$
				}
			}
		}
	}

	public static String getStringOfNode(IDocumentElementNode node) {
		if (node == null) {
			return null;
		}
		String s = node.getXMLTagName();
		for (IDocumentAttributeNode a : node.getNodeAttributes()) {
			s += a.getAttributeName() + "=" + a.getAttributeValue(); //$NON-NLS-1$
		}
		return s;
	}

	/**
	 * COPIED FROM ManifestSourcePage - from a getRangeElement body
	 */
	public static PluginObjectNode getNode(ManifestEditor editor, int offset, boolean hashCode) {
		ManifestSourcePage page = (ManifestSourcePage) editor.findPage("plugin-context"); //$NON-NLS-1$
		if (page != null) {
			IPluginModelBase model = (IPluginModelBase) page.getInputContext().getModel();
			PluginObjectNode node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getLibraries(),
					offset, hashCode);
			if (node == null) {
				node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getImports(), offset,
						hashCode);
			}
			if (node == null) {
				node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getExtensionPoints(),
						offset, hashCode);
			}
			if (node == null) {
				node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getExtensions(), offset,
						hashCode);
			}
			if (node == null) {
				node = (PluginObjectNode) PdeEditingMonitor.findNode(new IPluginObject[] { model.getPluginBase() },
						offset, hashCode);
			}
			return node;
		}
		return null;
	}

	public static PluginObjectNode getNode(IDocument d, IFile f, int num, boolean hashCode) throws CoreException {
		PluginModel model = new PluginModel(d, true);
		model.setUnderlyingResource(f);
		if (!model.isLoaded()) {
			model.load();
			model.setEnabled(true);
		}

		PluginObjectNode node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getLibraries(),
				num, hashCode);
		if (node == null) {
			node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getImports(), num, hashCode);
		}
		if (node == null) {
			node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getExtensionPoints(), num,
					hashCode);
		}
		if (node == null) {
			node = (PluginObjectNode) PdeEditingMonitor.findNode(model.getPluginBase().getExtensions(), num, hashCode);
		}
		if (node == null) {
			node = (PluginObjectNode) PdeEditingMonitor.findNode(new IPluginObject[] { model.getPluginBase() }, num,
					hashCode);
		}
		return node;
	}

	/**
	 * COPIED FROM ManifestSourcePage
	 */
	private static IDocumentElementNode findNode(IPluginObject[] nodes, int offset, boolean hashCode) {
		for (IPluginObject node3 : nodes) {
			IDocumentElementNode node = (IDocumentElementNode) node3;
			IDocumentElementNode[] children = node.getChildNodes();

			// changed region - added to check the children to make it work
			// properly
			IDocumentElementNode node2 = null;
			if (children.length > 0) {
				node2 = PdeEditingMonitor.findNode(children, offset, hashCode);
				// end changed region
			}

			if (node2 != null && node2 instanceof IPluginObject) {
				return node2;
			}

			if (!hashCode) {
				if (offset >= node.getOffset() && offset < node.getOffset() + node.getLength()) {
					return node;
				}
			} else {
				if (getStringOfNode(node).hashCode() == offset) {
					return node;
				}
			}
		}
		return null;
	}

	/**
	 * Copy of previous, taking different arguments
	 */
	private static IDocumentElementNode findNode(IDocumentElementNode[] nodes, int offset, boolean hashCode) {
		for (IDocumentElementNode node : nodes) {
			IDocumentElementNode[] children = node.getChildNodes();
			IDocumentElementNode node2 = null;
			if (children.length > 0) {
				node2 = PdeEditingMonitor.findNode(children, offset, hashCode);
			}
			if (node2 != null) {
				return node2;
			}

			if (!hashCode) {
				if (offset >= node.getOffset() && offset < node.getOffset() + node.getLength()) {
					return node;
				}
			} else {
				if (getStringOfNode(node).hashCode() == offset) {
					return node;
				}
			}
		}
		return null;
	}
}

// XXX used if we support the xmen editor
// if (part instanceof XMLTextEditor) {
// XMLTextEditor editor = (XMLTextEditor)part;
// TextSelection textSelection =
// (TextSelection)editor.getSelectionProvider().getSelection();
// if (textSelection != null) {
//
// try{
// // get the model for the xml elements
// XMLReconciler rec = editor.getModel();
//                    
// int start = textSelection.getStartLine();
// int end = textSelection.getEndLine();
//                    
// // get the node that was selected
// XMLNode node = rec.getNodeFromLine(start);
//                    
// ContextCorePlugin.getTaskscapeManager().handleElementSelected(
// node.getHandle(), node.getCanName(),
// IDegreeOfInterest.Value.Selections,
// ITaskscapeNode.Kind.XML);
//                              
// }catch(Exception e){
// //handle properly
// }
// }
