/*******************************************************************************
 * Copyright (c) 2009 Red Hat, 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:
 *    Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.ui.editor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;

public class SuppressionKindRule implements IRule {
	private Map<String, List<String>> kinds;
	private IToken token;
	private WordRule subrule;
	
	private static final IToken DUMMY_TOKEN = new Token(null);
	private static final char[] COLON = new char[] { ':' };
	
	public SuppressionKindRule(Map<String, List<String>> suppKinds, IToken successToken) {
		kinds = suppKinds;
		token = successToken;
		
		final List<String> suppKindsList = new ArrayList<String>();
		Set<String> tools = suppKinds.keySet();
		for (String tool : tools) {
			suppKindsList.addAll(suppKinds.get(tool));
		}
		
		subrule = new WordRule(new IWordDetector() {
		
			public boolean isWordStart(char c) {
				for (String kind : suppKindsList) {
					if (c == kind.charAt(0)) {
						return true;
					}
				}
				return false;
			}
		
			public boolean isWordPart(char c) {
				return Character.isJavaIdentifierPart(c);
			}
			
		});
		
		for (String kind : suppKindsList) {
			subrule.addWord(kind, DUMMY_TOKEN);
		}
	}
	
	public IToken evaluate(ICharacterScanner scanner) {
		IToken result = subrule.evaluate(scanner);
		StringBuffer tool = new StringBuffer();
		StringBuffer kind = new StringBuffer();
		
		if (!result.isUndefined()) {
			boolean match = true;
			
			// move to beginning of token
			int tokenLength = ((SuppressionsElementScanner) scanner).getTokenLength();
			for (int i = 0; i < tokenLength; i++) {
				scanner.unread();
			}
			
			// check first char before token is a ':'
			match = checkColon(scanner);
			if (match) {
				// move to beginning of line
				int preTokenLength = 0;
				while (scanner.getColumn() > 0) {
					scanner.unread();
					preTokenLength++;
				}

				// ensure any leading characters are whitespace
				boolean foundChar = false;
				while (preTokenLength-- > 1) { // skip colon
					int ch = scanner.read();
					if (match && !Character.isWhitespace(ch)) {
						foundChar = true;
						tool.append(Character.toChars(ch));
					}
					else if (foundChar) {
						// encountered whitespace after tool name started
						match = false;
					}
				}
				
				scanner.read(); // consume colon

				// reset to end of token
				for (int i = 0; i < tokenLength; i++) {
					int ch = scanner.read();
					if (match) {
						kind.append(Character.toChars(ch));
					}
				}
				
				// assert kind is valid for tool
				if (match) {
					List<String> kindList = kinds.get(tool.toString());
					if (kindList == null || !kindList.contains(kind.toString())) {
						match = false;
					}
				}
				else {
					// reset to beginning of token
					unreadBuffer(scanner, tokenLength);
				}
			}
			
			if (!match) {
				result = Token.UNDEFINED;
			}
			else {
				result = token;
			}
		}
		
		return result;
	}

	private void unreadBuffer(ICharacterScanner scanner, int length) {
		for (int i = 0; i < length; i++) {
			scanner.unread();
		}		
	}

	private boolean checkColon(ICharacterScanner scanner) {
		if (scanner.getColumn() == 0) {
			// nothing to read
			return false;
		}
		scanner.unread();
		int ch = scanner.read();
		return Arrays.equals(Character.toChars(ch), COLON);
	}

}
