/*******************************************************************************
 * Copyright (c) 2008 xored software, 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:
 *     xored software, Inc. - initial API and Implementation (Alex Panchenko)
 *     xored software, Inc. - Fixed bug for end of file less whan 4 symbol comments (Andrei Sobolev)
 *     xored software, Inc. - introduced findCommentStart(), improved tag matching (Alex Panchenko)
 *******************************************************************************/
package org.eclipse.dltk.compiler.task;

import java.util.List;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.core.DLTKCore;

public class TodoTaskSimpleParser {

	private final ITaskReporter taskReporter;
	private final boolean caseSensitive;
	private final char[][] tags;
	private final int minTagLength;
	private final int[] priorities;

	public TodoTaskSimpleParser(ITaskReporter taskReporter,
			ITodoTaskPreferences preferences) {
		this.taskReporter = taskReporter;
		this.caseSensitive = preferences.isCaseSensitive();
		final List tags = preferences.getTaskTags();
		if (!tags.isEmpty()) {
			final int tagCount = tags.size();
			this.tags = new char[tagCount][];
			this.priorities = new int[tagCount];
			int minTagLength = Integer.MAX_VALUE;
			for (int i = 0; i < tagCount; ++i) {
				final TodoTask task = (TodoTask) tags.get(i);
				String tagName = task.name;
				if (!caseSensitive) {
					tagName = tagName.toUpperCase();
				}
				if (tagName.length() < minTagLength) {
					minTagLength = tagName.length();
				}
				this.tags[i] = tagName.toCharArray();
				if (TodoTask.PRIORITY_HIGH.equals(task.priority)) {
					priorities[i] = IMarker.PRIORITY_HIGH;
				} else if (TodoTask.PRIORITY_LOW.equals(task.priority)) {
					priorities[i] = IMarker.PRIORITY_LOW;
				} else {
					priorities[i] = IMarker.PRIORITY_NORMAL;
				}
			}
			this.minTagLength = minTagLength;
		} else {
			this.tags = null;
			this.minTagLength = 0;
			this.priorities = null;
		}
	}

	public boolean isValid() {
		return tags != null && tags.length > 0;
	}

	private int lineNumber;
	private int contentPos;
	private int contentEnd;

	public void parse(char[] content) {
		lineNumber = 0;
		contentPos = 0;
		contentEnd = content.length;
		while (contentPos < contentEnd) {
			int begin = contentPos;
			final int end = findEndOfLine(content);
			if (begin < end) {
				begin = findCommentStart(content, begin, end);
				if (begin > 0) {
					begin = skipSpaces(content, begin, end);
					if (begin + minTagLength <= end) {
						processLine(content, begin, end);
					}
				}
			}
			++lineNumber;
		}
	}

	/**
	 * returns the next index after the comment of -1 if there is no comment in
	 * this line
	 * 
	 * @param content
	 * @param begin
	 * @param end
	 * @return
	 */
	protected int findCommentStart(char[] content, int begin, int end) {
		begin = skipSpaces(content, begin, end);
		if (begin < end && content[begin] == '#') {
			return begin + 1;
		} else {
			return -1;
		}
	}

	private static int skipSpaces(char[] content, int pos, final int end) {
		while (pos < end && Character.isWhitespace(content[pos])) {
			++pos;
		}
		return pos;
	}

	private void processLine(char[] content, int begin, final int end) {
		for (int i = 0; i < tags.length; ++i) {
			final char[] tag = tags[i];
			if (begin + tag.length <= end
					&& compareTag(content, begin, end, tag)) {
				final String msg = new String(content, begin, end - begin);
				try {
					taskReporter.reportTask(msg, lineNumber, priorities[i],
							begin, end);
				} catch (CoreException e) {
					DLTKCore.error("Error in reportTask()", e); //$NON-NLS-1$
				}
			}
		}
	}

	private boolean compareTag(char[] content, int pos, int end,
			final char[] tag) {
		final int tagLen = tag.length;
		if (caseSensitive) {
			for (int j = 0; j < tagLen; ++j) {
				if (content[pos + j] != tag[j]) {
					return false;
				}
			}
		} else {
			for (int j = 0; j < tagLen; ++j) {
				if (Character.toUpperCase(content[pos + j]) != tag[j]) {
					return false;
				}
			}
		}
		if (pos + tagLen < end
				&& Character.isJavaIdentifierPart(tag[tagLen - 1])) {
			if (Character.isJavaIdentifierPart(content[pos + tagLen])) {
				return false;
			}
		}
		return true;
	}

	private int findEndOfLine(char[] content) {
		while (contentPos < contentEnd) {
			if (content[contentPos] == '\r') {
				final int endLine = contentPos;
				++contentPos;
				if (contentPos < contentEnd && content[contentPos] == '\n') {
					++contentPos;
				}
				return endLine;
			} else if (content[contentPos] == '\n') {
				final int endLine = contentPos;
				++contentPos;
				return endLine;
			} else {
				++contentPos;
			}
		}
		return contentPos;
	}

}
