/*******************************************************************************
 * 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.Collections;
import java.util.List;

import org.eclipse.core.internal.resources.File;
import org.eclipse.core.internal.resources.Marker;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.AbstractContextStructureBridge;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.internal.ide.ui.XmlNodeHelper;
import org.eclipse.pde.internal.core.text.build.BuildEntry;
import org.eclipse.pde.internal.core.text.plugin.PluginNode;
import org.eclipse.pde.internal.core.text.plugin.PluginObjectNode;
import org.eclipse.pde.internal.ui.editor.PDEFormPage;
import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.views.markers.internal.ConcreteMarker;

/**
 * @author Mik Kersten
 */
@SuppressWarnings("restriction")
public class PdeStructureBridge extends AbstractContextStructureBridge {

	public final static String CONTENT_TYPE = "plugin.xml"; //$NON-NLS-1$

	@Override
	public String getContentType() {
		return CONTENT_TYPE;
	}

	@Override
	public String getContentType(String elementHandle) {
		if (elementHandle.endsWith(".xml")) { //$NON-NLS-1$
			return parentContentType;
		} else {
			return CONTENT_TYPE;
		}
	}

	@Override
	public List<String> getChildHandles(String handle) {
		return Collections.emptyList();
	}

	@Override
	public String getParentHandle(String handle) {
		// we can only get the parent if we have a PluginObjectNode

		Object object = getObjectForHandle(handle);
		if (object instanceof PluginObjectNode) {
			PluginObjectNode parent = (PluginObjectNode) ((PluginObjectNode) object).getParentNode();
			if (parent != null) {
				return getHandleIdentifier(parent);
			} else {
				// the parent is the plugin.xml file, so return that handle
				int delimeterIndex = handle.indexOf(";"); //$NON-NLS-1$
				if (delimeterIndex != -1) {
					String parentHandle = handle.substring(0, delimeterIndex);
					return parentHandle;
				} else {
					return null;
				}
			}
		} else if (object instanceof PluginNode) {
			PluginNode parent = (PluginNode) ((PluginNode) object).getParentNode();
			if (parent != null) {
				return getHandleIdentifier(parent);
			} else {
				// the parent is the plugin.xml file, so return that handle
				int delimeterIndex = handle.indexOf(";"); //$NON-NLS-1$
				if (delimeterIndex != -1) {
					String parentHandle = handle.substring(0, delimeterIndex);
					return parentHandle;
				} else {
					return null;
				}
			}
		} else if (object instanceof IFile) {
			// String fileHandle = parentBridge.getParentHandle(handle);
			AbstractContextStructureBridge parentBridge = ContextCore.getStructureBridge(parentContentType);
			return parentBridge.getParentHandle(handle);
		} else {
			return null;
		}
	}

	/**
	 * @see org.eclipse.mylyn.context.core.AbstractContextStructureBridge#getObjectForHandle(java.lang.String)
	 */
	@Override
	public Object getObjectForHandle(String handle) {
		if (handle == null) {
			return null;
		}
		int first = handle.indexOf(";"); //$NON-NLS-1$
		String filename = ""; //$NON-NLS-1$
		if (first == -1) {
			AbstractContextStructureBridge parentBridge = ContextCore.getStructureBridge(parentContentType);
			return parentBridge.getObjectForHandle(handle);
		} else {
			// extract the filename from the handle since it represents a node
			filename = handle.substring(0, first);
		}

		try {
			// get the file and create a FileEditorInput
			IPath path = new Path(filename);
			IFile f = (IFile) ((Workspace) ResourcesPlugin.getWorkspace()).newResource(path, IResource.FILE);

			// get the start line for the element
			int start = Integer.parseInt(handle.substring(first + 1));

			// // get the content and the document so that we can get the offset
			// String content = XmlNodeHelper.getContents(f.getContents());
			// IDocument d = new Document(content);

			// get the offsets for the element
			// make sure that we are on a character and not whitespace
			// int offset = d.getLineOffset(start);
			// while(d.getChar(offset) == ' ')
			// offset++;

			// get the current editor which should be the ManifestEditor so that
			// we can get the element that we want
			IEditorPart editorPart = null;
			try {
				editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			} catch (NullPointerException e) {
				// do nothing, this just means that there is no active page
			}
			if (editorPart != null && editorPart instanceof ManifestEditor) {
				PluginObjectNode node = PdeEditingMonitor.getNode((ManifestEditor) editorPart, start, true);
				// get the element based on the offset
				return node;
			} else {
				String content = XmlNodeHelper.getContents(f.getContents());
				IDocument d = new Document(content);
				PluginObjectNode node = PdeEditingMonitor.getNode(d, f, start, true);
				return node;
			}
		} catch (Exception e) {
			// ignore, means file doesn't exist
			// ContextCorePlugin.log(e, "handle failed");
		}
		return null;
	}

	/**
	 * Handle is filename;hashcodeOfElementAndAttributes
	 * 
	 * @see org.eclipse.mylyn.context.core.AbstractContextStructureBridge#getHandleIdentifier(java.lang.Object)
	 */
	@Override
	public String getHandleIdentifier(Object object) {
		// we can only create handles for PluginObjectNodes and plugin.xml files
		if (object instanceof XmlNodeHelper) {
			return ((XmlNodeHelper) object).getHandle();
		} else if (object instanceof PluginObjectNode) {
			PluginObjectNode node = (PluginObjectNode) object;
			try {
				// get the handle for the PluginObjectNode
				if (node.getModel() == null || node.getModel().getUnderlyingResource() == null
						|| node.getModel().getUnderlyingResource().getFullPath() == null) {
					// ContextCorePlugin.log("PDE xml node's resource or model is
					// null: " + node.getName(), this);
					return null;
				}
				IPath path = new Path(node.getModel().getUnderlyingResource().getFullPath().toString());
				IFile file = (IFile) ((Workspace) ResourcesPlugin.getWorkspace()).newResource(path, IResource.FILE);
				String handle = new XmlNodeHelper(file.getFullPath().toString(),
						PdeEditingMonitor.getStringOfNode(node).hashCode()).getHandle();
				return handle;
			} catch (Exception e) {
				StatusHandler.log(new Status(IStatus.ERROR, PdeUiBridgePlugin.ID_PLUGIN, "Could not get handle", e)); //$NON-NLS-1$
			}
		} else if (object instanceof PluginNode) {
			PluginNode node = (PluginNode) object;
			try {
				if (node.getModel() == null || node.getModel().getUnderlyingResource() == null
						|| node.getModel().getUnderlyingResource().getFullPath() == null) {
					// ContextCorePlugin.log("PDE xml node's resource or model is
					// null: " + node.getName(), this);
					return null;
				}
				IPath path = new Path(node.getModel().getUnderlyingResource().getFullPath().toString());
				IFile file = (IFile) ((Workspace) ResourcesPlugin.getWorkspace()).newResource(path, IResource.FILE);
				String handle = new XmlNodeHelper(file.getFullPath().toString(),
						PdeEditingMonitor.getStringOfNode(node).hashCode()).getHandle();
				return handle;
			} catch (Exception e) {
				StatusHandler.log(new Status(IStatus.ERROR, PdeUiBridgePlugin.ID_PLUGIN, "Could not get handle", e)); //$NON-NLS-1$
			}

		} else if (object instanceof File) {
			// get the handle for the file if it is plugin.xml
			File file = (File) object;
			if (file.getFullPath().toString().endsWith("plugin.xml")) { //$NON-NLS-1$
				return file.getFullPath().toString();
			}
		}
		return null;
	}

	@Override
	public String getLabel(Object object) {
		if (object instanceof PluginObjectNode) {
			PluginObjectNode node = (PluginObjectNode) object;
			String name = node.getXMLAttributeValue("name"); //$NON-NLS-1$
			if (name == null) {
				name = node.getXMLTagName();
			}
			name = node.getModel().getUnderlyingResource().getName() + ": " + name; //$NON-NLS-1$
			return name;
		} else if (object instanceof File) {
			File file = (File) object;
			if (file.getFullPath().toString().endsWith("plugin.xml")) { //$NON-NLS-1$
				return "plugin.xml"; //$NON-NLS-1$
			}
		}
		return ""; //$NON-NLS-1$
	}

	/**
	 * TODO: make a non-handle based test
	 */
	@Override
	public boolean canBeLandmark(String handle) {
		if (handle == null) {
			return false;
		} else {
			return handle.indexOf(';') == -1;
		}
	}

	@Override
	public boolean acceptsObject(Object object) {
		// we only accept PluginObjectNodes and plugin.xml Files
		if (object instanceof PluginNode || object instanceof PluginObjectNode || object instanceof BuildEntry
				|| object instanceof PDEFormPage) {
			return true;
		} else if (object instanceof XmlNodeHelper) {
			if (((XmlNodeHelper) object).getFilename().endsWith("plugin.xml")) { //$NON-NLS-1$
				return true;
			}
		} else if (object instanceof File) {
			File file = (File) object;
			if (file.getFullPath().toString().endsWith("plugin.xml")) { //$NON-NLS-1$
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean canFilter(Object element) {
		return true;
	}

	@Override
	public boolean isDocument(String handle) {
		return handle.indexOf(';') == -1;
	}

	@Override
	public String getHandleForOffsetInObject(Object object, int offset) {
		if (object == null) {
			return null;
		}

		IResource markerResource = null;
		try {
			if (object instanceof ConcreteMarker) {
				markerResource = ((ConcreteMarker) object).getMarker().getResource();
			} else if (object instanceof Marker) {
				markerResource = ((Marker) object).getResource();
			}
		} catch (Exception e) {
			return null;
		}

		if (markerResource != null) {
			// we can only get a handle for a marker with the resource
			// plugin.xml
			try {
				if (markerResource instanceof IFile) {
					IFile file = (IFile) markerResource;
					if (file.getFullPath().toString().endsWith("plugin.xml")) { //$NON-NLS-1$
						return file.getFullPath().toString();
					} else {
						return null;
					}
				}
				return null;
			} catch (Throwable t) {
				StatusHandler.log(new Status(IStatus.WARNING, PdeUiBridgePlugin.ID_PLUGIN,
						"Could not find element for: " + object)); //$NON-NLS-1$
				return null;
			}
		} else if (object instanceof IFile) {
			try {
				IFile file = (IFile) object;
				if (file.getFullPath().toString().endsWith("plugin.xml")) { //$NON-NLS-1$
					String content = XmlNodeHelper.getContents(file.getContents());
					IDocument d = new Document(content);
					PluginObjectNode node = PdeEditingMonitor.getNode(d, file, offset, false);
					String handle = new XmlNodeHelper(file.getFullPath().toString(), PdeEditingMonitor.getStringOfNode(
							node).hashCode()).getHandle();
					return handle;
				}
			} catch (Exception e) {
				StatusHandler.log(new Status(IStatus.ERROR, PdeUiBridgePlugin.ID_PLUGIN,
						"Unable to get handle for offset in object", e)); //$NON-NLS-1$
			}
		}
		return null;
	}

//	public IProject getProjectForObject(Object object) {
//		while (!(object instanceof IFile)) {
//			String handle = getParentHandle(getHandleIdentifier(object));
//			if (handle == null)
//				break;
//			object = getObjectForHandle(handle);
//		}
//		if (object instanceof IFile && acceptsObject(object)) {
//			return ((IFile) object).getProject();
//		}
//		return null;
//	}

	public boolean containsProblem(IInteractionElement node) {
		// TODO Auto-generated method stub
		return false;
	}
}
