/*******************************************************************************
 * Copyright (c) 2004, 2010 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
 *     David Green - fix for bug 266693
 *     Abner Ballardo - fix for bug 288427
 *******************************************************************************/

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

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.mylyn.commons.workbench.browser.UrlHyperlink;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.AbstractTaskHyperlinkDetector;

/**
 * Detects URLs based on a regular expression.
 * 
 * @author David Green
 */
public class TaskUrlHyperlinkDetector extends AbstractTaskHyperlinkDetector {

	// based on RFC 3986
	// even though it's valid, the platform hyperlink detector doesn't detect hyperlinks that end with '.', ',' or ')'
	// so we do the same here
	private static final Pattern URL_PATTERN = Pattern.compile("([a-zA-Z][a-zA-Z+.-]{0,10}://[a-zA-Z0-9%._~!$&?#'()*+,;:@/=-]*[a-zA-Z0-9%_~!$&?#'(*+;:@/=-])"); //$NON-NLS-1$

	private static final String CLOSED_PARENTHESIS_PATTERN = "[^)]"; //$NON-NLS-1$

	private static final String OPEN_PARENTHESIS_PATTERN = "[^(]"; //$NON-NLS-1$

	private static final String EMPTY_STRING = ""; //$NON-NLS-1$

	public TaskUrlHyperlinkDetector() {
	}

	@Override
	protected List<IHyperlink> detectHyperlinks(ITextViewer textViewer, String content, int indexInContent,
			int contentOffset) {
		List<IHyperlink> links = null;
		Matcher m = URL_PATTERN.matcher(content);
		while (m.find()) {
			if (isInRegion(indexInContent, m)) {
				String urlString = getUrlString(content, m);
				IHyperlink link = null;
				if (getAdapter(TaskRepository.class) != null) {
					try {
						new URL(urlString);
						link = createTaskUrlHyperlink(contentOffset, m, urlString);
					} catch (MalformedURLException e) {
						// ignore
					}

				} else {
					if (TasksUiInternal.isTaskUrl(urlString)) {
						link = createTaskUrlHyperlink(contentOffset, m, urlString);
					}
				}

				if (link != null) {
					if (links == null) {
						links = new ArrayList<IHyperlink>();
					}
					links.add(link);
				}
			}
		}
		return links;
	}

	private String getUrlString(String content, Matcher m) {
		String urlString = m.group(1);
		// check if the urlString has more opening parenthesis than closing 
		int parenthesisDiff = urlString.replaceAll(OPEN_PARENTHESIS_PATTERN, EMPTY_STRING).length()
				- urlString.replaceAll(CLOSED_PARENTHESIS_PATTERN, EMPTY_STRING).length();

		if (parenthesisDiff > 0) {
			// if any open paranthesis were not closed assume that trailing closing parenthesis are part of URL
			for (int i = m.end(); i - m.end() < parenthesisDiff; i++) {
				if (i >= content.length() || content.charAt(i) != ')') {
					break;
				}
				urlString += ')';
			}
		}
		return urlString;
	}

	private static boolean isInRegion(int offsetInText, Matcher m) {
		return (offsetInText == -1) || (offsetInText >= m.start() && offsetInText <= m.end());
	}

	private static IHyperlink createTaskUrlHyperlink(int textOffset, Matcher m, String urlString) {
		return new UrlHyperlink(new Region(textOffset + m.start(), urlString.length()), urlString);
	}

}
