/*******************************************************************************
 * Copyright (c) 2013 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;

import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.ast.Statement;

public abstract class CommitRollbackParser implements TerminalTokens, ParserBasicInformation {
	
	// resumeOnSyntaxError codes:
	protected static final int HALT = 0;     // halt and throw up hands.
	protected static final int RESTART = 1;  // stacks adjusted, alternate goal from check point.
	protected static final int RESUME = 2;   // stacks untouched, just continue from where left off.

	public Scanner scanner;
	public int currentToken;
	
	public CommitRollbackParser snapShot;
	private static final int[] RECOVERY_TOKENS = new int [] { TokenNameSEMICOLON, TokenNameRPAREN,};
	
	protected CommitRollbackParser createSnapShotParser() {
		return new Parser();
	}
	
	protected void commit() {
		if (this.snapShot == null) {
			this.snapShot = createSnapShotParser();
		}
		this.snapShot.copyState(this);
	}
	
	public void copyState(CommitRollbackParser commitRollbackParser) {
		// Subclasses should implement.
	}

	protected int getNextToken() {
		try {
			return this.scanner.getNextToken();
		} catch (InvalidInputException e) {
			return TokenNameEOF;
		}
	}
	
	protected void shouldStackAssistNode() {
		// Not relevant here.
	}
	
	// We get here on real syntax error or syntax error triggered by fake EOF at completion site, never due to triggered recovery.
	protected int fallBackToSpringForward(Statement unused) {
		int nextToken;
		boolean atCompletionSite = false;
		int automatonState = automatonState();
				
		// If triggered fake EOF at completion site, see if the real next token would have passed muster.
		if (this.currentToken == TokenNameEOF) {
			if (this.scanner.eofPosition < this.scanner.source.length) {
				atCompletionSite = true;
				this.scanner.eofPosition = this.scanner.source.length;
				nextToken = getNextToken();
				if (automatonWillShift(nextToken, automatonState)) {
					this.currentToken = nextToken;
					return RESUME;
				}
			} else {
				nextToken = TokenNameEOF;
			}
		} else {
			nextToken = this.currentToken;
		}
		if (nextToken == TokenNameEOF)
			return HALT; // don't know how to proceed.
		this.scanner.ungetToken(nextToken); // spit out what has been bitten more than we can chew.
		// OK, next token is no good to resume "in place", attempt some local repair. FIXME: need to make sure we don't get stuck keep reducing empty statements !!
		for (int i = 0, length = RECOVERY_TOKENS.length; i < length; i++) {
			if (automatonWillShift(RECOVERY_TOKENS[i], automatonState)) {
				this.currentToken = RECOVERY_TOKENS[i];
				return RESUME;
			}
		}
		// OK, no in place resumption, no local repair, fast forward to next statement.
		if (this.snapShot == null)
			return RESTART;

		this.copyState(this.snapShot);
		if (atCompletionSite) {
			this.currentToken = TokenNameSEMICOLON;
			shouldStackAssistNode();
			return RESUME;
		}
		this.currentToken = this.scanner.fastForward(unused);
		return RESUME;
	}

	public abstract int automatonState();

	public abstract boolean automatonWillShift(int nextToken, int lastAction);
}

