/*******************************************************************************
 * Copyright (C) 2011, Bernard Leach <leachbj@bouncycastle.org>
 * Copyright (C) 2011, Dariusz Luksza <dariusz@luksza.org>
 * Copyright (C) 2012, Robin Stocker <robin@nibor.org>
 *
 * 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
 *******************************************************************************/
package org.eclipse.egit.core.op;

import static org.eclipse.egit.core.project.RepositoryMapping.findRepositoryMapping;
import static org.eclipse.jgit.lib.Constants.HEAD;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.internal.job.RuleUtil;
import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.RmCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;

/**
 * Remove from Git Index operation (unstage).
 */
public class RemoveFromIndexOperation implements IEGitOperation {

	private final Map<Repository, Collection<String>> pathsByRepository;

	/**
	 * @param paths
	 *            list of paths that should be removed from index
	 * @since 2.2
	 */
	public RemoveFromIndexOperation(Collection<IPath> paths) {
		this.pathsByRepository = ResourceUtil.splitPathsByRepository(paths);
	}

	/**
	 * @param repo
	 *            repository in with given files should be removed from index
	 * @param resources
	 *            list of resources that should be removed from index
	 * @deprecated use {@link #RemoveFromIndexOperation(Collection)} instead
	 */
	@Deprecated
	public RemoveFromIndexOperation(Repository repo, IResource[] resources) {
		this.pathsByRepository = ResourceUtil.splitResourcesByRepository(resources);
	}

	public void execute(IProgressMonitor m) throws CoreException {
		IProgressMonitor monitor = (m != null) ? m : new NullProgressMonitor();

		monitor.beginTask(
				CoreText.RemoveFromIndexOperation_removingFilesFromIndex,
				pathsByRepository.size());

		for (Map.Entry<Repository, Collection<String>> entry : pathsByRepository.entrySet()) {
			Repository repository = entry.getKey();
			Collection<String> paths = entry.getValue();

			GitCommand<?> command = prepareCommand(repository, paths);

			try {
				command.call();
				monitor.worked(1);
			} catch (GitAPIException e) {
				Activator.logError(e.getMessage(), e);
			} finally {
				findRepositoryMapping(repository).fireRepositoryChanged();
			}
		}

		monitor.done();
	}

	public ISchedulingRule getSchedulingRule() {
		return RuleUtil.getRuleForRepositories(pathsByRepository.keySet());
	}

	private static GitCommand<?> prepareCommand(Repository repository,
			Collection<String> paths) {
		Git git = new Git(repository);
		if (hasHead(repository)) {
			ResetCommand resetCommand = git.reset();
			resetCommand.setRef(HEAD);
			for (String path : paths)
				resetCommand.addPath(getCommandPath(path));
			return resetCommand;
		} else {
			RmCommand rmCommand = git.rm();
			rmCommand.setCached(true);
			for (String path : paths)
				rmCommand.addFilepattern(getCommandPath(path));
			return rmCommand;
		}
	}

	private static boolean hasHead(Repository repository) {
		try {
			Ref head = repository.getRef(HEAD);
			return head != null && head.getObjectId() != null;
		} catch (IOException e) {
			return false;
		}
	}

	private static String getCommandPath(String path) {
		if ("".equals(path)) // Working directory //$NON-NLS-1$
			return "."; //$NON-NLS-1$
		else
			return path;
	}
}
