/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ant.internal.ui.launchConfigurations;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.tools.ant.util.FileUtils;
import org.eclipse.ant.internal.core.IAntCoreConstants;
import org.eclipse.ant.internal.launching.AntLaunch;
import org.eclipse.ant.internal.launching.AntLaunchingUtil;
import org.eclipse.ant.internal.launching.LinkDescriptor;
import org.eclipse.ant.internal.ui.AntUtil;
import org.eclipse.ant.internal.ui.ExternalHyperlink;
import org.eclipse.core.resources.IFile;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.ui.console.FileLink;
import org.eclipse.debug.ui.console.IConsole;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.console.IHyperlink;

/**
 * Manages task links per process. As messages are logged to the console from
 * build events, hyperlinks are created to link task names to the associated ant
 * buildfile. The build logger registers a task hyperlink with this manager for
 * each build event associated with a task. When the associated line is later
 * appended to the console, the corresponding text region in the console
 * document is determined (as the length of a console document can not be
 * determined beforehand), and the hyperlink is added to the document.
 * The new line is added to the console, information from that line 
 * may be stored to process future incoming tasks hyperlinks.
 */
public class TaskLinkManager {

	private static Map fFileNameToIFile = new HashMap();
	
	/**
	 * Not to be called.
	 */
	private TaskLinkManager() {
		super();
	}

	private static IHyperlink createHyperlink(LinkDescriptor linkDescriptor) {
		String fileName = linkDescriptor.getFileName();
		int lineNumber = linkDescriptor.getLineNumber();

		IHyperlink taskLink = null;
		if (lineNumber == -1) {
			// fileName will actually be the String representation of Location
			taskLink = AntUtil.getLocationLink(fileName, null);
		} else {
			IFile file = (IFile) fFileNameToIFile.get(fileName);
			if (file == null) {
				file = AntLaunchingUtil.getFileForLocation(fileName, null);
				if (file != null) {
					fFileNameToIFile.put(fileName, file);
					taskLink = new FileLink(file, null, -1, -1, lineNumber);
				} else if(fileName!=null){
					File javaIOFile = FileUtils.getFileUtils().resolveFile(null, fileName);
					if (javaIOFile.exists()) {
						taskLink = new ExternalHyperlink(javaIOFile, lineNumber);
					}
				}
			} else {
				taskLink = new FileLink(file, null, -1, -1, lineNumber);
			}
		}
		return taskLink;
	}
	
	private static boolean addLink(IConsole console, IRegion lineRegion, LinkDescriptor descriptor) {
		try {
			String text = console.getDocument().get(lineRegion.getOffset(), lineRegion.getLength());
			if (text.trim().equals(descriptor.getLine())) {
				int offset = lineRegion.getOffset() + descriptor.getOffset();
				IHyperlink link = createHyperlink(descriptor);
				if (link != null) {
					console.addLink(link, offset, descriptor.getLength());
				}
				return true;
			}
		} catch (BadLocationException e) {
		}
		return false;
	}	

	/**
	 * A new line has been added to the given console. Adds any task hyperlink
	 * associated with the line, to the console.
	 * The new line may be stored to process future incoming tasks hyperlinks.
	 * 
	 * @param console
	 * @param newLine
	 */
	public static synchronized void processNewLine(IConsole console, IRegion newLine) {
		AntLaunch launch = (AntLaunch) console.getProcess().getLaunch();
		List links = launch.getLinkDescriptors();

		if (linkBuildFileMessage(console, newLine)) {
			return;
		}

		if (links == null || links.isEmpty()) {
			return;
		}

		for (Iterator i = links.iterator(); i.hasNext();) {
			LinkDescriptor descriptor = (LinkDescriptor) i.next();
			if (addLink(console, newLine, descriptor)) {
				launch.removeLinkDescriptor(descriptor);
				return;
			}
		}
	}

	/**
	 * Disposes any information stored for the given process.
	 * 
	 * @param process
	 */
	public static void dispose(IProcess process) {
		AntLaunch launch = (AntLaunch) process.getLaunch();
		launch.clearLinkDescriptors();
	}

	private static boolean linkBuildFileMessage(IConsole console, IRegion region) {
		
		String message= IAntCoreConstants.EMPTY_STRING;
		int offset= region.getOffset();
		try {
			message = console.getDocument().get(offset, region.getLength());
		} catch (BadLocationException e) {
		}
		if (message.startsWith("Buildfile:")) { //$NON-NLS-1$
			String fileName = message.substring(10).trim();
			IFile file = AntUtil.getFileForLocation(fileName, null);
			if (file != null) {
				FileLink link = new FileLink(file, null,  -1, -1, -1);
				console.addLink(link, offset + 11, fileName.length());
				return true;
			}
		}
		return false;
	}
}
