/*******************************************************************************
 * Copyright (c) 2006, 2016 Red Hat, Inc.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat Incorporated - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.autotools.ui.editors;

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.IWhitespaceDetector;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.Token;

public class InlineDataRule implements IRule {
	/**
	 * The default token to be returned on success and if nothing else has been
	 * specified.
	 */
	protected IToken token;

	protected IWordDetector fDetector;
	protected IWhitespaceDetector fWsDetector = new AutoconfWhitespaceDetector();

	/** The column constraint */
	protected int fColumn = UNDEFINED;

	/** Internal setting for the un-initialized column constraint */
	protected static final int UNDEFINED = -1;

	/** Buffer used for pattern detection */
	private StringBuilder fBuffer = new StringBuilder();

	private String fStartingSequence = "<<";

	public InlineDataRule(IToken inToken) {
		token = inToken;
	}

	// Confirm an EOL delimeter after already matching first delimeter character.
	protected boolean confirmDelimeter(ICharacterScanner scanner, char[] delimeter) {
		int c = scanner.read();
		StringBuilder buffer = new StringBuilder();
		buffer.append((char) c);
		for (int i = 1; i < delimeter.length; ++i) {
			if (c == delimeter[i]) {
				c = scanner.read();
				buffer.append((char) c);
			} else {
				for (int j = buffer.length() - 1; j >= 0; j--)
					scanner.unread();
				return false;
			}
		}
		scanner.unread();
		return true;
	}

	@Override
	public IToken evaluate(ICharacterScanner scanner) {
		int c = scanner.read();
		fBuffer.setLength(0);

		// Looking for <<WORD or <<-WORD or <<'WORD' at end of line.
		for (int i = 0; i < fStartingSequence.length(); i++) {
			fBuffer.append((char) c);
			if (fStartingSequence.charAt(i) != c) {
				unreadBuffer(scanner);
				return Token.UNDEFINED;
			}
			c = scanner.read();
		}

		char[][] lineDelimeters = scanner.getLegalLineDelimiters();
		StringBuilder endMarkerBuffer = new StringBuilder();
		if (c == '-') {
			fBuffer.append((char) c);
			c = scanner.read();
		} else if (c == '\'') {
			fBuffer.append((char) c);
			c = scanner.read();
		}

		while (c != ICharacterScanner.EOF && Character.isJavaIdentifierPart((char) c) && c != '\'') {
			fBuffer.append((char) c);
			endMarkerBuffer.append((char) c);
			c = scanner.read();
		}

		if (c == '\'') {
			fBuffer.append((char) c);
			c = scanner.read();
		}

		if (endMarkerBuffer.length() == 0) {
			unreadBuffer(scanner);
			return Token.UNDEFINED;
		}

		// At this point we read until we find id by itself on its own line
		boolean eol = false;
		boolean finished = false;
		boolean foundMarker = false;
		String endMarker = endMarkerBuffer.toString();
		while (!finished && c != ICharacterScanner.EOF) {
			for (int i = 0; i < lineDelimeters.length; ++i) {
				if (c == lineDelimeters[i][0]) {
					if (confirmDelimeter(scanner, lineDelimeters[i])) {
						c = scanner.read();
						eol = true;
						break;
					}
				}
			}
			if (eol) {
				eol = false;
				if (foundMarker) {
					// We get here if we have found the marker by itself
					// on the line
					finished = true;
				} else {
					foundMarker = false;
					int j = 0;
					while (j < endMarker.length()) {
						if (c == endMarker.charAt(j)) {
							c = scanner.read();
							++j;
						} else {
							break;
						}
					}
					if (j == endMarker.length()) {
						foundMarker = true;
					}
				}
			} else {
				// otherwise ignore all other characters
				eol = false;
				foundMarker = false;
				c = scanner.read();
			}
		}

		// unread last character
		scanner.unread();
		return token;
	}

	/**
	 * Returns the characters in the buffer to the scanner.
	 *
	 * @param scanner
	 *            the scanner to be used
	 */
	protected void unreadBuffer(ICharacterScanner scanner) {
		for (int i = fBuffer.length() - 1; i >= 0; i--)
			scanner.unread();
	}

}
