/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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, Inc. - initial API and implementation
 ******************************************************************************/
package org.jboss.tools.usage.util.reader;

import java.io.Reader;

/**
 * @author Andre Dietisheim
 */
public class ReadUntilAlternativesImpl extends ReadUntilImpl {

	private char[][] allAlternatives;
	private char[] currentAlternative;
	private int alternativesIndex = -1;

	public ReadUntilAlternativesImpl(Reader reader, String... stringAlternatives) {
		super(reader);
		initAlternativesCharSequences(stringAlternatives);
	}

	private void initAlternativesCharSequences(String... stringAlternatives) {
		this.allAlternatives = new char[stringAlternatives.length][];
		for (int i = 0; i < stringAlternatives.length; i++) {
			this.allAlternatives[i] = stringAlternatives[i].toCharArray();
		}
	}

	@Override
	protected int getNumberOfCharactersToMatch() {
		if (currentAlternative != null) {
			return currentAlternative.length;
		} else {
			return 0;
		}
	}

	@Override
	protected boolean doesMatch(char character) {
		if (currentAlternative == null || currentAlternative[getMatchingIndex()] != character) {
			// current alternative does not match new character, select a new
			// alternative
			boolean newAlternativeSelected = matchAlternative(character);
			if (!newAlternativeSelected) {
				// no alternative matches current character + new one
				setMatchingIndex(0);
			}
			return newAlternativeSelected;
		} else {
			return true;
		}
	}

	/**
	 * Returns whether the given character matches an alternative (in other
	 * words the given character matches an alternative at the current matching
	 * index).
	 * 
	 * @param character
	 *            the character
	 * @return true, if successful
	 */
	private boolean matchAlternative(char character) {
		for (int i = alternativesIndex + 1; i < allAlternatives.length; i++) {
			char[] alternative = allAlternatives[i];
			if (doesMatch(character, alternative)) {
				this.currentAlternative = alternative;
				this.alternativesIndex = i;
				return true;
			}

		}
		this.currentAlternative = null;
		this.alternativesIndex = -1;
		return false;
	}

	/**
	 * Returns whether the given potentially matching alternative (String)
	 * matches the currently selected alternative and the additional character.
	 * 
	 * @param character
	 * @param potentiallyMatchingAlternative
	 *            the new alternative that could match
	 * @return
	 */
	private boolean doesMatch(char character, char[] potentiallyMatchingAlternative) {
		int currentMatchingIndex = getMatchingIndex();
		for (int j = 0; j < currentMatchingIndex; j++) {
			if (potentiallyMatchingAlternative[j] != currentAlternative[j]) {
				return false;
			}
		}
		return potentiallyMatchingAlternative[currentMatchingIndex] == character;
	}

	@Override
	protected char[] getCharactersToMatch() {
		return currentAlternative;
	}

	public String getAlternative() {
		if (alternativesIndex >= 0) {
			return new String(allAlternatives[alternativesIndex]);
		} else {
			return null;
		}
	}
}