/*******************************************************************************
 * Copyright (c) 2003, 2012 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.egit.ui.internal;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.resources.mapping.ResourceMappingContext;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.egit.ui.Activator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISaveableFilter;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.ide.ResourceUtil;

/*
 * Copied from org.eclipse.ui.ide.IDE.SaveFilter, see
 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=386609
 * Replace with the above when we can depend on Eclipse 4.3.
 */
/**
 * A saveable filter that selects savables that contain resources that
 * are descendants of the roots of the filter.
 * @since 3.3
 *
 */
class SaveFilter implements ISaveableFilter {
	private final IResource[] roots;

	/**
	 * Create the filter
	 * @param roots the save roots
	 */
	public SaveFilter(IResource[] roots) {
		this.roots = roots;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.ISaveableFilter#select(org.eclipse.ui.Saveable, org.eclipse.ui.IWorkbenchPart[])
	 */
	public boolean select(Saveable saveable,
			IWorkbenchPart[] containingParts) {
		if (isDescendantOfRoots(saveable)) {
			return true;
		}
		// For backwards compatibility, we need to check the parts
		for (int i = 0; i < containingParts.length; i++) {
			IWorkbenchPart workbenchPart = containingParts[i];
			if (workbenchPart instanceof IEditorPart) {
				IEditorPart editorPart = (IEditorPart) workbenchPart;
				if (isEditingDescendantOf(editorPart)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Return whether the given saveable contains any resources that
	 * are descendants of the root resources.
	 * @param saveable the saveable
	 * @return whether the given saveable contains any resources that
	 * are descendants of the root resources
	 */
	private boolean isDescendantOfRoots(Saveable saveable) {
		// First, try and adapt the saveable to a resource mapping.
		ResourceMapping mapping = ResourceUtil.getResourceMapping(saveable);
		if (mapping != null) {
			try {
				ResourceTraversal[] traversals = mapping.getTraversals(
						ResourceMappingContext.LOCAL_CONTEXT, null);
				for (int i = 0; i < traversals.length; i++) {
					ResourceTraversal traversal = traversals[i];
					IResource[] resources = traversal.getResources();
					for (int j = 0; j < resources.length; j++) {
						IResource resource = resources[j];
						if (isDescendantOfRoots(resource)) {
							return true;
						}
					}
				}
			} catch (CoreException e) {
				Activator
						.logError(
								NLS
										.bind(
												"An internal error occurred while determining the resources for {0}", saveable.getName()), e); //$NON-NLS-1$
			}
		} else {
			// If there is no mapping, try to adapt to a resource or file directly
			IFile file = ResourceUtil.getFile(saveable);
			if (file != null) {
				return isDescendantOfRoots(file);
			}
		}
		return false;
	}

	/**
	 * Return whether the given resource is either equal to or a descendant of
	 * one of the given roots.
	 *
	 * @param resource the resource to be tested
	 * @return whether the given resource is either equal to or a descendant of
	 *         one of the given roots
	 */
	private boolean isDescendantOfRoots(IResource resource) {
		for (int l = 0; l < roots.length; l++) {
			IResource root = roots[l];
			if (root.getFullPath().isPrefixOf(resource.getFullPath())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return whether the given dirty editor part is editing resources that are
	 * descendants of the given roots.
	 *
	 * @param part the dirty editor part
	 * @return whether the given dirty editor part is editing resources that are
	 *         descendants of the given roots
	 */
	private boolean isEditingDescendantOf(IEditorPart part) {
		IFile file = ResourceUtil.getFile(part.getEditorInput());
		if (file != null) {
			return isDescendantOfRoots(file);
		}
		return false;
	}

}
