/******************************************************************************
 *  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.mylyn.internal.github.ui.pr;

import java.io.IOException;
import java.text.MessageFormat;

import org.eclipse.core.commands.common.CommandException;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.egit.github.core.PullRequest;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.commit.CommitEditor;
import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.mylyn.internal.github.core.GitHub;
import org.eclipse.mylyn.internal.github.core.issue.IssueConnector;
import org.eclipse.mylyn.internal.github.core.pr.PullRequestComposite;
import org.eclipse.mylyn.internal.github.core.pr.PullRequestUtils;
import org.eclipse.mylyn.internal.github.ui.GitHubUi;
import org.eclipse.mylyn.internal.github.ui.TaskDataHandler;
import org.eclipse.mylyn.internal.tasks.ui.editors.AbstractTaskEditorSection;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;

/**
 * Task editor section to view commits attached to a pull request
 */
public class CommitAttributePart extends AbstractTaskEditorSection {

	private CommandContributionItem fetchCommits;

	private CommandContributionItem checkoutPr;

	private CommandContributionItem mergePr;

	private CommandContributionItem rebasePr;

	private PullRequestComposite request;

	/**
	 * Create commit attribute part
	 *
	 * @param request
	 */
	public CommitAttributePart(PullRequestComposite request) {
		setPartName(Messages.CommitAttributePart_PartName);
		this.request = request;
	}

	protected Control createContent(FormToolkit toolkit, Composite parent) {
		Composite displayArea = toolkit.createComposite(parent);
		GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(displayArea);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(displayArea);

		if (request != null) {
			Composite refArea = toolkit.createComposite(displayArea);
			GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(4)
					.applyTo(refArea);
			GridDataFactory.swtDefaults().grab(true, false).applyTo(refArea);
			Image branchIcon = UIIcons.BRANCH.createImage();
			UIUtils.hookDisposal(refArea, branchIcon);
			CLabel sourceLabel = new CLabel(refArea, SWT.NONE);
			toolkit.adapt(sourceLabel, false, false);
			sourceLabel.setText(Messages.CommitAttributePart_LabelSource);
			sourceLabel.setImage(branchIcon);
			sourceLabel.setForeground(toolkit.getColors().getColor(
					IFormColors.TITLE));
			toolkit.createText(refArea, request.getRequest().getHead()
					.getLabel(), SWT.READ_ONLY);
			CLabel destLabel = new CLabel(refArea, SWT.NONE);
			toolkit.adapt(destLabel, false, false);
			destLabel.setText(Messages.CommitAttributePart_LabelDestination);
			destLabel.setImage(branchIcon);
			destLabel.setForeground(toolkit.getColors().getColor(
					IFormColors.TITLE));
			GridDataFactory.swtDefaults().indent(15, 0).applyTo(destLabel);
			toolkit.createText(refArea, request.getRequest().getBase()
					.getLabel(), SWT.READ_ONLY);
		}

		Composite treeArea = toolkit.createComposite(displayArea);
		toolkit.paintBordersFor(treeArea);
		GridLayoutFactory.fillDefaults().spacing(0, 0)
				.extendedMargins(2, 2, 2, 7).applyTo(treeArea);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(treeArea);

		TreeViewer commitViewer = new TreeViewer(toolkit.createTree(treeArea,
				SWT.V_SCROLL | SWT.H_SCROLL | toolkit.getBorderStyle()));
		commitViewer.setContentProvider(new WorkbenchContentProvider());
		commitViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
				new WorkbenchLabelProvider()));
		GridDataFactory.fillDefaults().grab(true, true)
				.applyTo(commitViewer.getControl());
		commitViewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER,
				FormToolkit.TREE_BORDER);
		commitViewer.addOpenListener(new IOpenListener() {

			public void open(final OpenEvent event) {
				PullRequest pr = request.getRequest();
				Repository repo = PullRequestUtils.getRepository(pr);
				if (repo != null)
					openCommits(repo, ((IStructuredSelection) event
							.getSelection()).toArray());
				else
					PullRequestConnectorUi.showNoRepositoryDialog(pr);
			}
		});

		int size = 0;
		if (request != null) {
			PullRequestAdapter root = new PullRequestAdapter(request);
			size = root.getChildren(root).length;
			commitViewer.setInput(root);
			Point treeSize = commitViewer.getControl().computeSize(SWT.DEFAULT,
					SWT.DEFAULT);
			((GridData) commitViewer.getControl().getLayoutData()).heightHint = Math
					.min(treeSize.y, 200);
		}
		getSection().setText(
				MessageFormat.format(
						Messages.CommitAttributePart_SectionCommits, size));
		return displayArea;
	}

	private void openCommits(final Repository repository,
			final Object[] elements) {
		if (elements.length == 0)
			return;
		if (repository == null)
			return;
		RevWalk walk = new RevWalk(repository);
		try {
			for (Object element : elements) {
				String id = ((PullRequestCommitAdapter) element).getCommit()
						.getSha();
				try {
					CommitEditor.openQuiet(new RepositoryCommit(repository,
							walk.parseCommit(ObjectId.fromString(id))));
				} catch (MissingObjectException ignored) {
					boolean fetch = MessageDialog.openQuestion(getControl()
							.getShell(),
							Messages.CommitAttributePart_TitleFetch,
							Messages.CommitAttributePart_MessageFetch);
					if (fetch) {
						fetchCommits(new Runnable() {

							public void run() {
								PlatformUI.getWorkbench().getDisplay()
										.asyncExec(new Runnable() {

											public void run() {
												openCommits(repository,
														elements);
											}
										});
							}
						});
					}

				}
			}
		} catch (IOException e) {
			GitHubUi.logError(e);
		} finally {
			walk.release();
		}
	}

	private CommandContributionItem createCommandContributionItem(
			String commandId) {
		CommandContributionItemParameter parameter = new CommandContributionItemParameter(
				getTaskEditorPage().getEditorSite(), commandId, commandId,
				CommandContributionItem.STYLE_PUSH);
		return new CommandContributionItem(parameter);
	}

	protected void fillToolBar(ToolBarManager toolBarManager) {
		if (TasksUiUtil.isOutgoingNewTask(getTaskEditorPage().getTask(),
				IssueConnector.KIND))
			return;
		if (request == null)
			return;

		checkoutPr = createCommandContributionItem(CheckoutPullRequestHandler.ID);
		fetchCommits = createCommandContributionItem(FetchPullRequestHandler.ID);
		mergePr = createCommandContributionItem(MergePullRequestHandler.ID);
		rebasePr = createCommandContributionItem(RebasePullRequestHandler.ID);

		// Disable actions for now
		// toolBarManager.add(checkoutPr);
		toolBarManager.add(fetchCommits);
		// toolBarManager.add(mergePr);
		// toolBarManager.add(rebasePr);
	}

	@Override
	protected String getInfoOverlayText() {
		return request != null ? request.getRequest().getHead().getLabel()
				: null;
	}

	protected boolean shouldExpandOnCreate() {
		return true;
	}

	private void fetchCommits(final Runnable postHandler) {
		IHandlerService handlerService = (IHandlerService) getTaskEditorPage()
				.getEditorSite().getService(IHandlerService.class);
		try {
			IEvaluationContext context = TaskDataHandler.createContext(
					new StructuredSelection(getTaskData()), handlerService);
			if (postHandler != null)
				context.addVariable(TaskDataHandler.POST_HANDLER_CALLBACK,
						postHandler);
			handlerService.executeCommandInContext(fetchCommits.getCommand(),
					new Event(), context);
		} catch (CommandException e) {
			GitHub.logError(e);
		}
	}
}
