/******************************************************************************
 *  Copyright (c) 2011 GitHub Inc.
 *  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:
 *    Kevin Sawicki (GitHub Inc.) - initial API and implementation
 *****************************************************************************/
package org.eclipse.egit.ui.internal.dialogs;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.WorkbenchStyledLabelProvider;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.WorkbenchAdapter;
import org.eclipse.ui.model.WorkbenchContentProvider;

/**
 * Dialog to display when a revert fails
 */
public class RevertFailureDialog extends MessageDialog {

	/**
	 * Show dialog for failure result
	 *
	 * @param shell
	 * @param commit
	 * @param result
	 */
	public static void show(Shell shell, RevCommit commit, MergeResult result) {
		String message;
		Map<String, MergeFailureReason> reasons = result != null ? result
				.getFailingPaths() : null;
		if (reasons != null && !reasons.isEmpty())
			message = MessageFormat.format(UIText.RevertFailureDialog_Message,
					commit.abbreviate(7).name());
		else
			message = MessageFormat.format(
					UIText.RevertFailureDialog_MessageNoFiles, commit
							.abbreviate(7).name());

		RevertFailureDialog dialog = new RevertFailureDialog(shell, message,
				reasons);
		dialog.setShellStyle(dialog.getShellStyle() | SWT.SHEET | SWT.RESIZE);
		dialog.open();
	}

	private static class Path extends WorkbenchAdapter {

		private final String path;

		private Path(String path) {
			this.path = path;
		}

		public String getLabel(Object object) {
			return path;
		}

		public ImageDescriptor getImageDescriptor(Object object) {
			String name = new org.eclipse.core.runtime.Path(path).lastSegment();
			if (name != null) {
				return PlatformUI.getWorkbench().getEditorRegistry()
						.getImageDescriptor(name);
			} else
				return PlatformUI.getWorkbench().getSharedImages()
						.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
		}

		public StyledString getStyledText(Object object) {
			int lastSlash = path.lastIndexOf('/');
			StyledString styled = new StyledString();
			if (lastSlash != -1 && lastSlash + 1 < path.length()) {
				String name = path.substring(lastSlash + 1);
				styled.append(name).append(' ');
				styled.append("- ", StyledString.QUALIFIER_STYLER); //$NON-NLS-1$
				styled.append(path.substring(0, lastSlash),
						StyledString.QUALIFIER_STYLER);
			} else
				styled.append(path);
			return styled;
		}
	}

	private static class RevertFailure extends WorkbenchAdapter {

		private final MergeFailureReason reason;

		private final List<Path> paths;

		private RevertFailure(MergeFailureReason reason) {
			this.reason = reason;
			this.paths = new ArrayList<Path>();
		}

		private RevertFailure add(String path) {
			paths.add(new Path(path));
			return this;
		}

		public Object[] getChildren(Object object) {
			return paths.toArray();
		}

		public String getLabel(Object object) {
			switch (reason) {
			case DIRTY_INDEX:
				return UIText.RevertFailureDialog_ReasonChangesInIndex;
			case DIRTY_WORKTREE:
				return UIText.RevertFailureDialog_ReasonChangesInWorkingDirectory;
			case COULD_NOT_DELETE:
				return UIText.RevertFailureDialog_ReasonDeleteFailure;
			default:
				return super.getLabel(object);
			}
		}

		public StyledString getStyledText(Object object) {
			StyledString styled = new StyledString(getLabel(object));
			styled.append(' ');
			styled.append(MessageFormat.format("({0})", //$NON-NLS-1$
					Integer.valueOf(paths.size())), StyledString.COUNTER_STYLER);
			return styled;
		}
	}

	private final Map<String, MergeFailureReason> reasons;

	/**
	 * Create dialog for merge result
	 *
	 * @param shell
	 * @param message
	 * @param reasons
	 */
	public RevertFailureDialog(Shell shell, String message,
			Map<String, MergeFailureReason> reasons) {
		super(shell, UIText.RevertFailureDialog_Title, null, message, ERROR,
				new String[] { IDialogConstants.OK_LABEL }, 0);
		this.reasons = reasons;
	}

	protected Control createCustomArea(Composite parent) {
		if (reasons == null || reasons.isEmpty())
			return null;

		Composite fileArea = new Composite(parent, SWT.NONE);

		GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 80)
				.applyTo(fileArea);
		GridLayoutFactory.fillDefaults().applyTo(fileArea);
		TreeViewer viewer = new TreeViewer(fileArea);
		viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
		GridDataFactory.fillDefaults().grab(true, true)
				.applyTo(viewer.getControl());
		viewer.setContentProvider(new WorkbenchContentProvider() {

			public Object[] getElements(Object element) {
				return ((Collection) element).toArray();
			}

		});
		final IStyledLabelProvider styleProvider = new WorkbenchStyledLabelProvider() {

			public StyledString getStyledText(Object element) {
				// TODO Replace with use of IWorkbenchAdapter3 when is no longer
				// supported
				if (element instanceof RevertFailure)
					return ((RevertFailure) element).getStyledText(element);
				if (element instanceof Path)
					return ((Path) element).getStyledText(element);

				return super.getStyledText(element);
			}
		};
		viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
				styleProvider));
		viewer.setSorter(new ViewerSorter());

		Map<MergeFailureReason, RevertFailure> failures = new HashMap<MergeFailureReason, RevertFailure>();
		for (Entry<String, MergeFailureReason> reason : reasons.entrySet()) {
			RevertFailure failure = failures.get(reason.getValue());
			if (failure == null) {
				failure = new RevertFailure(reason.getValue());
				failures.put(reason.getValue(), failure);
			}
			failure.add(reason.getKey());
		}
		viewer.setInput(failures.values());

		return fileArea;
	}
}
