/*******************************************************************************
 * Copyright (c) 2004, 2009 Tasktop Technologies 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:
 *     Tasktop Technologies - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.tasks.ui.notifications;

import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;

import org.eclipse.mylyn.internal.tasks.core.data.TaskAttributeDiff;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataDiff;
import org.eclipse.mylyn.tasks.core.IRepositoryPerson;
import org.eclipse.mylyn.tasks.core.ITaskComment;
import org.eclipse.mylyn.tasks.core.data.ITaskAttributeDiff;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Drawable;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Composite;

/**
 * @author Steffen Pingel
 */
public class TaskDiffUtil {

	private static final int MAX_CHANGED_ATTRIBUTES = 2;

	// could use the ellipsis glyph on some platforms "\u2026"
	private static final String ELLIPSIS = "..."; //$NON-NLS-1$ 

	private static int DRAW_FLAGS = SWT.DRAW_MNEMONIC | SWT.DRAW_TAB | SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER;

	public static String toString(TaskDataDiff diff) {
		return toString(diff, 60, true);
	}

	// TODO implement trim based on text width
	public static String toString(TaskDataDiff diff, int maxWidth, boolean includeNewest) {
		StringBuilder sb = new StringBuilder();
		String sep = ""; //$NON-NLS-1$
		// append first comment
		int newCommentCount = diff.getNewComments().size();
		if (newCommentCount > 0) {
			Iterator<ITaskComment> iter = diff.getNewComments().iterator();
			ITaskComment comment = iter.next();
			if (includeNewest) {
				while (iter.hasNext()) {
					comment = iter.next();
				}
			}

			sb.append(TaskDiffUtil.trim(TaskDiffUtil.commentToString(comment), 60));
			if (newCommentCount > 1) {
				sb.append(" (" + (newCommentCount - 1) + Messages.TaskDataDiff_more_); //$NON-NLS-1$
			}
			sep = "\n"; //$NON-NLS-1$
		}
		// append changed attributes		
		int n = 0;
		for (ITaskAttributeDiff changedAttribute : diff.getChangedAttributes()) {
			TaskAttributeDiff attributeDiff = (TaskAttributeDiff) changedAttribute;
			String label = attributeDiff.getLabel();
			if (label != null) {
				sb.append(sep);
				sb.append(" "); //$NON-NLS-1$
				sb.append(label);
				sb.append(" "); //$NON-NLS-1$
				sb.append(TaskDiffUtil.trim(TaskDiffUtil.listToString(attributeDiff.getRemovedValues()), 28));
				sb.append(" -> "); //$NON-NLS-1$
				sb.append(TaskDiffUtil.trim(TaskDiffUtil.listToString(attributeDiff.getAddedValues()), 28));
				if (++n == MAX_CHANGED_ATTRIBUTES) {
					break;
				}
				sep = "\n"; //$NON-NLS-1$
			}
		}
		return sb.toString();
	}

	public static String commentToString(ITaskComment comment) {
		StringBuilder sb = new StringBuilder();
		sb.append(MessageFormat.format(Messages.TaskDiffUtil_Comment_by_X, personToString(comment.getAuthor())));
		sb.append(": "); //$NON-NLS-1$
		sb.append(cleanCommentText(comment.getText()));
		return sb.toString();
	}

	private static String personToString(IRepositoryPerson author) {
		if (author == null) {
			return Messages.TaskDiffUtil_Unknown;
		} else if (author.getName() != null) {
			return author.getName();
		}
		return author.getPersonId();
	}

	public static String cleanCommentText(String value) {
		String text = ""; //$NON-NLS-1$
		String[] lines = value.split("\n"); //$NON-NLS-1$
		boolean attachment = false;
		boolean needSeparator = false;
		for (String line : lines) {
			// skip comments and info lines
			if (attachment) {
				text += Messages.TaskDiffUtil_attachment + line;
				needSeparator = true;
				attachment = false;
			} else if (line.startsWith(">") // //$NON-NLS-1$
					|| line.matches("^\\s*\\(In reply to comment.*")) { //$NON-NLS-1$
				needSeparator = true;
				continue;
			} else if (line.startsWith("Created an attachment (id=")) { //$NON-NLS-1$
				attachment = true;
			} else {
				if (needSeparator) {
					if (!text.matches(".*\\p{Punct}\\s*")) { //$NON-NLS-1$
						text = text.trim();
						if (text.length() > 0) {
							text += "."; //$NON-NLS-1$
						}
					}
				}
				text += " " + line; //$NON-NLS-1$
				attachment = false;
				needSeparator = false;
			}
		}
		return foldSpaces(text);
	}

	public static String listToString(List<String> values) {
		if (values == null) {
			return ""; //$NON-NLS-1$
		}

		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for (String value : values) {
			if (!first) {
				sb.append(", "); //$NON-NLS-1$
			} else {
				first = false;
			}
			sb.append(foldSpaces(value));
		}
		return sb.toString();
	}

	public static String foldSpaces(String value) {
		return value.replaceAll("\\s+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$
	}

	public static String trim(String value, int length) {
		if (value.length() > length) {
			value = value.substring(0, length - 3) + "..."; //$NON-NLS-1$
		}
		return value;
	}

	/**
	 * Note: Copied from CLabel. Shorten the given text <code>t</code> so that its length doesn't exceed the given
	 * width. The default implementation replaces characters in the center of the original string with an ellipsis
	 * ("..."). Override if you need a different strategy.
	 * 
	 * @param gc
	 *            the gc to use for text measurement
	 * @param t
	 *            the text to shorten
	 * @param width
	 *            the width to shorten the text to, in pixels
	 * @return the shortened text
	 */
	public static final String shortenText2(Composite composite, String t, int width) {
		GC gc = new GC(composite);

		if (t == null) {
			return null;
		}
		int w = gc.textExtent(ELLIPSIS, DRAW_FLAGS).x;
		if (width <= w) {
			return t;
		}
		int l = t.length();
		int max = l / 2;
		int min = 0;
		int mid = (max + min) / 2 - 1;
		if (mid <= 0) {
			return t;
		}
		while (min < mid && mid < max) {
			String s1 = t.substring(0, mid);
			String s2 = t.substring(l - mid, l);
			int l1 = gc.textExtent(s1, DRAW_FLAGS).x;
			int l2 = gc.textExtent(s2, DRAW_FLAGS).x;
			if (l1 + w + l2 > width) {
				max = mid;
				mid = (max + min) / 2;
			} else if (l1 + w + l2 < width) {
				min = mid;
				mid = (max + min) / 2;
			} else {
				min = max;
			}
		}

		gc.dispose();

		if (mid == 0) {
			return t;
		}
		return t.substring(0, mid) + ELLIPSIS + t.substring(l - mid, l);
	}

	// From PerspectiveBarContributionItem
	public static String shortenText(Drawable composite, String text, int maxWidth) {
		String returnText = text;
		GC gc = new GC(composite);
		if (gc.textExtent(text).x > maxWidth) {
			for (int i = text.length(); i > 0; i--) {
				String subString = text.substring(0, i);
				subString = subString + "..."; //$NON-NLS-1$
				if (gc.textExtent(subString).x < maxWidth) {
					returnText = subString;
					break;
				}
			}
		}
		gc.dispose();
		return returnText;
	}

}
