/*******************************************************************************
 * Copyright (c) 2004, 2009 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
 *     Yatta Solutions -  WorkingSet filtering (bug 334024)
 *******************************************************************************/

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.eclipse.core.internal.resources.Marker;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
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.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.AbstractContextStructureBridge;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.resources.ui.ResourcesUi;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.views.markers.internal.ConcreteMarker;

/**
 * @author Mik Kersten
 * @author Carsten Reckord (bug 334024: focused package explorer not working if top level element is working set)
 */
public class ResourceStructureBridge extends AbstractContextStructureBridge {

	public final static String CONTENT_TYPE = ContextCore.CONTENT_TYPE_RESOURCE;

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

	@Override
	public String getParentHandle(String handle, boolean allowChildBridgeHandles) {
		if (allowChildBridgeHandles) {
			return getParentHandle(handle);
		} else {
			IResource resource = (IResource) getObjectForHandle(handle);
			if (resource != null) {
				IContainer parent = resource.getParent();
				return getHandleIdentifier(parent);
			}
		}

		return null;
	}

	@Override
	public String getParentHandle(String handle) {

		IResource resource = (IResource) getObjectForHandle(handle);
		if (resource != null) {
			IContainer parent = resource.getParent();
			// try to adapt to the corresponding content type's parent
			if (resource instanceof IFile) {
				for (String contentType : ContextCore.getChildContentTypes(CONTENT_TYPE)) {
					AbstractContextStructureBridge parentBridge = ContextCore.getStructureBridge(contentType);
					Object adaptedParent = parentBridge.getAdaptedParent(resource);
					// HACK: only returns first
					if (adaptedParent != null) {
						return parentBridge.getHandleIdentifier(adaptedParent);
					}
				}
			}
			return getHandleIdentifier(parent);
		} else {
			return null;
		}
	}

	@Override
	public List<String> getChildHandles(String handle) {
		Object object = getObjectForHandle(handle);
		if (object instanceof IResource) {
			IResource resource = (IResource) object;
			if (resource instanceof IContainer) {
				IContainer container = (IContainer) resource;
				IResource[] children;
				try {
					// make sure that we dont try to get the children of a closed project as this can cause an exception
					if (container.isAccessible()) {
						children = container.members();
						List<String> childHandles = new ArrayList<String>();
						for (IResource element : children) {
							String childHandle = getHandleIdentifier(element);
							if (childHandle != null) {
								childHandles.add(childHandle);
							}
						}
						return childHandles;
					}
				} catch (Exception e) {
					StatusHandler.log(new Status(IStatus.ERROR, ResourcesUiBridgePlugin.ID_PLUGIN, "" //$NON-NLS-1$
							+ "Could not get child", e)); //$NON-NLS-1$
				}
			} else if (resource instanceof IFile) {
				// delegate to child bridges
			}
		}
		return Collections.emptyList();
	}

	/**
	 * Uses java-style path for projects.
	 */
	@Override
	public String getHandleIdentifier(Object object) {
		if (object instanceof IProject) {
			String path = ((IResource) object).getFullPath().toPortableString();
			String javaCoreStylePath = "=" + path.substring(1); //$NON-NLS-1$
			return javaCoreStylePath;
		}
		if (object instanceof IResource) {
			return ((IResource) object).getFullPath().toPortableString();

		} else if (object instanceof IAdaptable) {
			IAdaptable adaptable = (IAdaptable) object;
			Object adapter = adaptable.getAdapter(IResource.class);
			if (adapter instanceof IResource) {
				return ((IResource) adapter).getFullPath().toPortableString();
			}

		}
		return null;
	}

	@Override
	public Object getObjectForHandle(String handle) {
		if (handle == null) {
			return null;
		}
		IPath path = new Path(handle);
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		if (path.segmentCount() == 1) {
			String projectName = handle.substring(1);
			try {
				return workspace.getRoot().getProject(projectName);
			} catch (IllegalArgumentException e) {
				return null;
			}
		} else if (path.segmentCount() > 1) {
			return workspace.getRoot().findMember(path);
		} else {
			return null;
		}
	}

	@Override
	public String getLabel(Object object) {
		if (object instanceof IResource) {
			return ((IResource) object).getName();
		} else {
			return ""; //$NON-NLS-1$
		}
	}

	@Override
	public boolean canBeLandmark(String handle) {
		Object element = getObjectForHandle(handle);
		return element instanceof IFile;
	}

	@Override
	public boolean acceptsObject(Object object) {
		if (object instanceof IResource || object instanceof IWorkingSet) {
			return true;
		}
		if (object instanceof IAdaptable) {
			IAdaptable adaptable = (IAdaptable) object;
			Object adapter = adaptable.getAdapter(IResource.class);
			if (adapter instanceof IResource) {
				return true;
			}

		}
		return false;
	}

	@Override
	public boolean canFilter(Object element) {
		if (element instanceof IWorkingSet) {
			try {
				IWorkingSet workingSet = (IWorkingSet) element;
				IAdaptable[] elements = workingSet.getElements();
				for (IAdaptable adaptable : elements) {
					final AbstractContextStructureBridge elementBridge = ContextCore.getStructureBridge(adaptable);
					String handle = elementBridge.getHandleIdentifier(adaptable);
					if (handle != null) {
						IInteractionElement interactionElement = ContextCore.getContextManager().getElement(handle);
						if (interactionElement != null && interactionElement.getInterest().isInteresting()) {
							return false;
						}
					}
				}
			} catch (Exception e) {
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean isDocument(String handle) {
		return getObjectForHandle(handle) instanceof IFile;
	}

	@Override
	public String getHandleForOffsetInObject(Object object, int offset) {
		IResource markerResource = null;
		try {
			if (object instanceof ConcreteMarker) {
				markerResource = ((ConcreteMarker) object).getMarker().getResource();
			} else if (object instanceof Marker) {
				markerResource = ((Marker) object).getResource();
			} else {
				return null;
			}
		} catch (Exception e) {
			return null;
		}

		// we can only get a handle for a marker with the resource plugin.xml
		try {
			if (markerResource instanceof IFile) {
				IFile file = (IFile) markerResource;
				return getHandleIdentifier(file);
			}
			return null;
		} catch (Throwable t) {
			StatusHandler.log(new Status(IStatus.ERROR, ResourcesUiBridgePlugin.ID_PLUGIN,
					"Could not find element for: \"" + object + "\"", t)); //$NON-NLS-1$ //$NON-NLS-2$
			return null;
		}
	}

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

	/**
	 * @deprecated use {@link ResourcesUi#getProjects(IInteractionContext)} instead
	 */
	@Deprecated
	public Set<IProject> getProjectsInActiveContext() {
		return ResourcesUi.getProjects(ContextCore.getContextManager().getActiveContext());
	}

}
