/*******************************************************************************
 * Copyright (C) 2012, 2014 Robin Stocker <robin@nibor.org> 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
 *******************************************************************************/
package org.eclipse.egit.ui.internal.history;

import org.eclipse.egit.ui.internal.GitLabels;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.text.AbstractHoverInformationControlManager;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.GitDateFormatter.Format;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Shell;

/**
 * For showing tool tips when hovering over cells in the commit graph table.
 */
class CommitGraphTableHoverManager extends
		AbstractHoverInformationControlManager {

	private final GitDateFormatter dateFormatter = new GitDateFormatter(
			Format.ISO);

	private final TableViewer tableViewer;

	private final SWTPlotRenderer renderer;

	CommitGraphTableHoverManager(TableViewer tableViewer,
			SWTPlotRenderer renderer) {
		super(new RefHoverInformationControlCreator());
		this.tableViewer = tableViewer;
		this.renderer = renderer;
	}

	@Override
	protected void computeInformation() {
		MouseEvent e = getHoverEvent();

		Information information = null;
		ViewerCell cell = tableViewer.getCell(new Point(e.x, e.y));
		if (cell != null) {
			SWTCommit commit = (SWTCommit) cell.getElement();
			if (commit != null)
				information = computeInformationForCommit(commit, cell, e);
		}

		// computeInformation must setInformation in all cases
		if (information != null)
			setInformation(information.information, information.subjectArea);
		else
			setInformation(null, null);
	}

	private Information computeInformationForCommit(SWTCommit commit,
			ViewerCell cell, MouseEvent e) {
		final int columnIndex = cell.getColumnIndex();
		switch (columnIndex) {
		case 1:
			return computeInformationForRef(commit, cell, e);
		case 2:
			return computeInformationForName(commit.getAuthorIdent(), cell);
		case 3:
			return computeInformationForDate(commit.getAuthorIdent(), cell);
		case 4:
			return computeInformationForName(commit.getCommitterIdent(), cell);
		case 5:
			return computeInformationForDate(commit.getCommitterIdent(), cell);
		}
		return null;
	}

	private Information computeInformationForRef(SWTCommit commit,
			ViewerCell cell, MouseEvent e) {
		if (commit.getRefCount() == 0)
			return null;
		Rectangle itemBounds = cell.getBounds();
		int relativeX = e.x - itemBounds.x;
		for (int i = 0; i < commit.getRefCount(); i++) {
			Ref ref = commit.getRef(i);
			Point textSpan = renderer.getRefHSpan(ref);
			if ((textSpan != null)
					&& (relativeX >= textSpan.x && relativeX <= textSpan.y)) {

				String hoverText = getHoverText(ref, i, commit);
				int x = itemBounds.x + textSpan.x;
				int width = textSpan.y - textSpan.x;
				Rectangle rectangle = new Rectangle(x, itemBounds.y, width,
						itemBounds.height);
				return new Information(hoverText, rectangle);
			}
		}
		return null;
	}

	private Information computeInformationForName(PersonIdent ident,
			ViewerCell cell) {
		String nameWithEmail = ident.getName()
				+ " <" + ident.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
		return new Information(nameWithEmail, cell.getBounds());
	}

	private Information computeInformationForDate(PersonIdent ident,
			ViewerCell cell) {
		String formattedDate = dateFormatter.formatDate(ident);
		return new Information(formattedDate, cell.getBounds());
	}

	private String getHoverText(Ref ref, int refIndex, SWTCommit commit) {
		if (ref.getName().startsWith(Constants.R_TAGS)
				&& renderer.isShownAsEllipsis(ref)) {
			StringBuilder sb = new StringBuilder(
					UIText.CommitGraphTable_HoverAdditionalTags);
			for (int i = refIndex; i < commit.getRefCount(); i++) {
				Ref tag = commit.getRef(i);
				String name = tag.getName();
				if (name.startsWith(Constants.R_TAGS)) {
					sb.append('\n');
					sb.append(name.substring(Constants.R_TAGS.length()));
				}
			}
			return sb.toString();
		} else {
			return getHoverTextForSingleRef(ref);
		}
	}

	private String getHoverTextForSingleRef(Ref r) {
		StringBuilder sb = new StringBuilder();
		String name = r.getName();
		sb.append(name);
		if (r.isSymbolic()) {
			sb.append(": "); //$NON-NLS-1$
			sb.append(r.getLeaf().getName());
		}
		String description = GitLabels.getRefDescription(r);
		if (description != null) {
			sb.append("\n"); //$NON-NLS-1$
			sb.append(description);
		}
		return sb.toString();
	}

	private static final class RefHoverInformationControlCreator extends
			AbstractReusableInformationControlCreator {
		@Override
		protected IInformationControl doCreateInformationControl(Shell parent) {
			return new DefaultInformationControl(parent);
		}
	}

	private static class Information {
		final Object information;
		final Rectangle subjectArea;

		private Information(Object information, Rectangle subjectArea) {
			this.information = information;
			this.subjectArea = subjectArea;
		}
	}
}
