/*******************************************************************************
 * Copyright (c) 2007, 2018 Red Hat, Inc.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Red Hat - initial API and implementation
 *******************************************************************************/

package org.eclipse.linuxtools.internal.rpm.ui.editor.rules;

import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IPredicateRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;

public class AuthorEmailRule implements IPredicateRule {

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

	/** The success token */
	private IToken token;

	protected static final char START_CHAR = '<';

	protected static final char END_CHAR = '>';

	protected static final char[] INTER_CHARS = { '@', '.' };

	protected static final int STATE_START = 0;

	protected static final int STATE_OPENED = 1;

	protected static final int STATE_AT = 2;

	protected static final int STATE_PERIOD = 3;

	protected static final int STATE_DONE = 4;

	public AuthorEmailRule(IToken token) {
		this.token = token;
	}

	@Override
	public IToken getSuccessToken() {
		return token;
	}

	@Override
	public IToken evaluate(ICharacterScanner scanner, boolean resume) {
		/*
		 * whether we think we're reading the ending sequence, i.e. the next section
		 * heading
		 */
		int state = STATE_START;
		fBuffer.setLength(0);
		int c;

		do {
			c = scanner.read();
			fBuffer.append((char) c);

			// we have reached the end of file or line prematurely, this is not
			// considered success
			if (c == ICharacterScanner.EOF || (char) c == '\n') {
				unreadBuffer(scanner, fBuffer);
				return Token.UNDEFINED;
			}

			// we encountered the opening character at the beginning
			if (state == STATE_START && (char) c == START_CHAR) {
				state++;
			} else if (state == STATE_OPENED) {
				// we encountered the first neccessary intermediary char
				if ((char) c == INTER_CHARS[0]) {
					state++;
				}

				// check if we have a valid char
				if (!(Character.isLetterOrDigit((char) c) || c == '.' || c == '_' || c == '-' || c == '@')) {
					unreadBuffer(scanner, fBuffer);
					return Token.UNDEFINED;
				}

				// we just keep reading

			} else if (state == STATE_AT) {
				// we encountered the second neccessary intermediary char
				if ((char) c == INTER_CHARS[1]) {
					state++;
				}

				// check if we have a valid char
				if (!(Character.isLetterOrDigit((char) c) || c == '.' || c == '_' || c == '-')) {
					unreadBuffer(scanner, fBuffer);
					return Token.UNDEFINED;
				}
				// we just keep reading
			} else if (state == STATE_PERIOD) {
				// the last char before the ending char cannot be a '.'
				if ((char) c == END_CHAR && fBuffer.charAt(fBuffer.length() - 1) != '.') {
					state++;
				} else if ((char) c == END_CHAR) {
					unreadBuffer(scanner, fBuffer);
					return Token.UNDEFINED;
				}
			} else {
				unreadBuffer(scanner, fBuffer);
				return Token.UNDEFINED;
			}

		} while (state != STATE_DONE);

		// we've gone through all states until we've reached STATE_DONE, success
		return token;
	}

	@Override
	public IToken evaluate(ICharacterScanner scanner) {
		return evaluate(scanner, false);
	}

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

}
