/*******************************************************************************
 * Copyright (c) 2011, 2013 SAP AG 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:
 *    Mathias Kinzler (SAP AG) - initial implementation
 *    Daniel Megert <daniel_megert@ch.ibm.com> - remove unnecessary @SuppressWarnings
 *    Markus Keller <markus_keller@ch.ibm.com> - Open multiple detail dialogs from MultiPullResultDialog at once
 *    Daniel Megert <daniel_megert@ch.ibm.com> - Use correct syntax when a single ref was updated
 *******************************************************************************/
package org.eclipse.egit.ui.internal.pull;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.merge.MergeResultDialog;
import org.eclipse.egit.ui.internal.rebase.RebaseResultDialog;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.RebaseResult;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

/**
 * Presents the result of a pull for multiple repositories
 * <p>
 * Each line corresponds to one Repository; there is either a {@link PullResult}
 * or an {@link IStatus} corresponding to each Repository.
 *
 * The user can select a line and check the {@link PullResult} for this line by
 * hitting a "Details" button (or double-clicking the line); if there is no
 * {@link PullResult} for this line, only the text of the {@link IStatus} can be
 * inspected (double-clicking does nothing, Details button is inactive).
 */
public class MultiPullResultDialog extends Dialog {
	private static final int DETAIL_BUTTON = 99;

	// the value is either a PullResult or an IStatus
	private final Map<Repository, Object> results = new LinkedHashMap<Repository, Object>();

	private TableViewer tv;

	private final static class LabelProvider extends BaseLabelProvider
			implements ITableLabelProvider {
		private final RepositoryUtil utils = Activator.getDefault()
				.getRepositoryUtil();

		@SuppressWarnings("unchecked")
		public Image getColumnImage(Object element, int columnIndex) {
			if (columnIndex != 3)
				return null;
			Entry<Repository, Object> item = (Entry<Repository, Object>) element;
			Object resultOrError = item.getValue();
			if (resultOrError instanceof IStatus)
				return PlatformUI.getWorkbench().getSharedImages().getImage(
						ISharedImages.IMG_ELCL_STOP);

			PullResult res = (PullResult) item.getValue();
			boolean success = res.isSuccessful();
			if (!success)
				return PlatformUI.getWorkbench().getSharedImages().getImage(
						ISharedImages.IMG_ELCL_STOP);
			return null;
		}

		@SuppressWarnings("unchecked")
		public String getColumnText(Object element, int columnIndex) {
			Entry<Repository, Object> item = (Entry<Repository, Object>) element;
			switch (columnIndex) {
			case 0:
				return utils.getRepositoryName(item.getKey());
			case 1: {
				if (item.getValue() instanceof IStatus)
					return UIText.MultiPullResultDialog_UnknownStatus;
				PullResult pullRes = (PullResult) item.getValue();
				if (pullRes.getFetchResult() == null)
					return UIText.MultiPullResultDialog_NothingFetchedStatus;
				else if (pullRes.getFetchResult().getTrackingRefUpdates()
						.isEmpty())
					return UIText.MultiPullResultDialog_NothingUpdatedStatus;
				else {
					int updated = pullRes.getFetchResult()
							.getTrackingRefUpdates().size();
					if ( updated == 1)
						return UIText.MultiPullResultDialog_UpdatedOneMessage;
					return NLS.bind(
							UIText.MultiPullResultDialog_UpdatedMessage,
							Integer.valueOf(updated));
				}
			}
			case 2: {
				if (item.getValue() instanceof IStatus)
					return UIText.MultiPullResultDialog_UnknownStatus;
				PullResult pullRes = (PullResult) item.getValue();
				if (pullRes.getMergeResult() != null) {
					return NLS.bind(
							UIText.MultiPullResultDialog_MergeResultMessage,
							MergeResultDialog.getStatusText(pullRes
									.getMergeResult().getMergeStatus()));
				} else if (pullRes.getRebaseResult() != null) {
					RebaseResult res = pullRes.getRebaseResult();
					return NLS.bind(
							UIText.MultiPullResultDialog_RebaseResultMessage,
							RebaseResultDialog.getStatusText(res.getStatus()));
				} else {
					return UIText.MultiPullResultDialog_NothingUpdatedStatus;
				}
			}
			case 3:
				if (item.getValue() instanceof IStatus) {
					IStatus status = (IStatus) item.getValue();
					return status.getMessage();
				}
				PullResult res = (PullResult) item.getValue();
				if (res.isSuccessful())
					return UIText.MultiPullResultDialog_OkStatus;
				else
					return UIText.MultiPullResultDialog_FailedStatus;
			default:
				return null;
			}
		}
	}

	/**
	 * @param parentShell
	 * @param results
	 *            maps {@link Repository}s to either {@link PullResult} or
	 *            {@link IStatus}
	 */
	protected MultiPullResultDialog(Shell parentShell,
			Map<Repository, Object> results) {
		super(parentShell);
		setShellStyle(getShellStyle() & ~SWT.APPLICATION_MODAL | SWT.SHELL_TRIM);
		setBlockOnOpen(false);
		this.results.putAll(results);
	}

	@Override
	public void create() {
		super.create();
		getButton(DETAIL_BUTTON).setEnabled(false);
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		Composite main = new Composite(parent, SWT.NONE);
		GridLayoutFactory.fillDefaults().applyTo(main);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(main);
		tv = new TableViewer(main, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
		tv.setContentProvider(ArrayContentProvider.getInstance());

		tv.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				IStructuredSelection sel = (IStructuredSelection) event
						.getSelection();
				boolean enabled = false;
				for (Entry<Repository, Object> entry : (List<Entry<Repository, Object>>) sel
						.toList())
					enabled |= entry.getValue() instanceof PullResult;
				getButton(DETAIL_BUTTON).setEnabled(enabled);
			}
		});

		tv.addOpenListener(new IOpenListener() {
			public void open(OpenEvent event) {
				buttonPressed(DETAIL_BUTTON);
			}
		});
		tv.setLabelProvider(new LabelProvider());
		Table table = tv.getTable();
		int linesToShow = Math.min(Math.max(results.size(), 5), 15);
		int heightHint = table.getItemHeight() * linesToShow;
		GridDataFactory.fillDefaults().grab(true, true).hint(800, heightHint)
				.applyTo(table);
		// repository
		TableColumn col = new TableColumn(table, SWT.NONE);
		col.setWidth(200);
		col.setText(UIText.MultiPullResultDialog_RepositoryColumnHeader);
		// fetch status
		col = new TableColumn(table, SWT.NONE);
		col.setWidth(200);
		col.setText(UIText.MultiPullResultDialog_FetchStatusColumnHeader);
		// update status
		col = new TableColumn(table, SWT.NONE);
		col.setWidth(200);
		col.setText(UIText.MultiPullResultDialog_UpdateStatusColumnHeader);
		// overall status
		col = new TableColumn(table, SWT.NONE);
		col.setWidth(150);
		col.setText(UIText.MultiPullResultDialog_OverallStatusColumnHeader);

		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		tv.setInput(results.entrySet());
		return main;
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		createButton(parent, DETAIL_BUTTON,
				UIText.MultiPullResultDialog_DetailsButton, false);
		createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
				true);
	}

	@Override
	protected void buttonPressed(int buttonId) {
		if (buttonId == DETAIL_BUTTON) {
			final Shell shell = getShell();
			Rectangle trim = shell.computeTrim(0, 0, 0, 0);
			int xOffset = 0;
			int xDelta = -trim.x + 3;
			int yOffset = 0;
			int yDelta = -trim.y - 3;

			final LinkedList<PullResultDialog> dialogs= new LinkedList<PullResultDialog>();
			IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
			for (Entry<Repository, Object> item : (List<Entry<Repository, Object>>) sel
					.toList()) {

				if (item.getValue() instanceof PullResult) {
					final int x = xOffset;
					final int y = yOffset;
					xOffset += xDelta;
					yOffset += yDelta;

					final PullResultDialog dialog = new PullResultDialog(shell,
							item.getKey(), (PullResult) item.getValue()) {
						private Point initialLocation;

						@Override
						protected Point getInitialLocation(Point initialSize) {
							initialLocation = super
									.getInitialLocation(initialSize);
							initialLocation.x += x;
							initialLocation.y += y;
							return initialLocation;
						}

						@Override
						public boolean close() {
							// restore shell location if we moved it:
							Shell resultShell = getShell();
							if (resultShell != null
									&& !resultShell.isDisposed()) {
								Point location = resultShell.getLocation();
								if (location.equals(initialLocation)) {
									resultShell.setVisible(false);
									resultShell.setLocation(location.x - x,
											location.y - y);
								}
							}
							boolean result = super.close();

							// activate next result dialog (not the multi-result dialog):

							// TODO: This doesn't work due to https://bugs.eclipse.org/388667 :
//							Shell[] subShells = shell.getShells();
//							if (subShells.length > 0) {
//								subShells[subShells.length - 1].setActive();
//							}

							dialogs.remove(this);
							if (dialogs.size() > 0)
								dialogs.getLast().getShell().setActive();

							return result;
						}
					};
					dialog.create();
					dialog.getShell().addShellListener(new ShellAdapter() {
						public void shellActivated(org.eclipse.swt.events.ShellEvent e) {
							dialogs.remove(dialog);
							dialogs.add(dialog);
						}
					});
					dialog.open();
				}
			}
		}
		super.buttonPressed(buttonId);
	}

	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText(UIText.MultiPullResultDialog_WindowTitle);
	}
}
