/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.parser;

import net.sourceforge.phpdt.internal.compiler.ast.ASTNode;
import net.sourceforge.phpdt.internal.compiler.ast.Block;
import net.sourceforge.phpdt.internal.compiler.ast.Statement;

/**
 * Internal structure for parsing recovery
 */
public class RecoveredElement {

	public RecoveredElement parent;

	public int bracketBalance;

	public boolean foundOpeningBrace;

	protected Parser recoveringParser;

	public RecoveredElement(RecoveredElement parent, int bracketBalance) {
		this(parent, bracketBalance, null);
	}

	public RecoveredElement(RecoveredElement parent, int bracketBalance,
			Parser parser) {
		this.parent = parent;
		this.bracketBalance = bracketBalance;
		this.recoveringParser = parser;
	}

	/*
	 * Record a method declaration
	 */
	// public RecoveredElement add(AbstractMethodDeclaration methodDeclaration,
	// int bracketBalance) {
	//
	// /* default behavior is to delegate recording to parent if any */
	// if (parent == null) {
	// return this; // ignore
	// } else {
	// this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(methodDeclaration.declarationSourceStart
	// - 1));
	// return this.parent.add(methodDeclaration, bracketBalance);
	// }
	// }
	/*
	 * Record a nested block declaration
	 */
	public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {

		/* default behavior is to delegate recording to parent if any */
		if (parent == null) {
			return this; // ignore
		} else {
			this
					.updateSourceEndIfNecessary(this
							.previousAvailableLineEnd(nestedBlockDeclaration.sourceStart - 1));
			return this.parent.add(nestedBlockDeclaration, bracketBalance);
		}
	}

	/*
	 * Record a field declaration
	 */
	// public RecoveredElement add(FieldDeclaration fieldDeclaration, int
	// bracketBalance) {
	//
	// /* default behavior is to delegate recording to parent if any */
	// if (parent == null) {
	// return this; // ignore
	// } else {
	// this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart
	// - 1));
	// return this.parent.add(fieldDeclaration, bracketBalance);
	// }
	// }
	// /*
	// * Record an import reference
	// */
	// public RecoveredElement add(ImportReference importReference, int
	// bracketBalance){
	//
	// /* default behavior is to delegate recording to parent if any */
	// if (parent == null) {
	// return this; // ignore
	// } else {
	// this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(importReference.declarationSourceStart
	// - 1));
	// return this.parent.add(importReference, bracketBalance);
	// }
	// }
	// /*
	// * Record a local declaration
	// */
	// public RecoveredElement add(LocalDeclaration localDeclaration, int
	// bracketBalance) {
	//
	// /* default behavior is to delegate recording to parent if any */
	// if (parent == null) {
	// return this; // ignore
	// } else {
	// this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart
	// - 1));
	// return this.parent.add(localDeclaration, bracketBalance);
	// }
	// }
	/*
	 * Record a statement
	 */
	public RecoveredElement add(Statement statement, int bracketBalance) {

		/* default behavior is to delegate recording to parent if any */
		if (parent == null) {
			return this; // ignore
		} else {
			this.updateSourceEndIfNecessary(this
					.previousAvailableLineEnd(statement.sourceStart - 1));
			return this.parent.add(statement, bracketBalance);
		}
	}

	/*
	 * Record a type declaration
	 */
	// public RecoveredElement add(TypeDeclaration typeDeclaration, int
	// bracketBalance){
	//
	// /* default behavior is to delegate recording to parent if any */
	// if (parent == null) {
	// return this; // ignore
	// } else {
	// this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart
	// - 1));
	// return this.parent.add(typeDeclaration, bracketBalance);
	// }
	// }
	/*
	 * Answer the depth of this element, considering the parent link.
	 */
	public int depth() {
		int depth = 0;
		RecoveredElement current = this;
		while ((current = current.parent) != null)
			depth++;
		return depth;
	}

	/*
	 * Answer the enclosing method node, or null if none
	 */
	// public RecoveredInitializer enclosingInitializer(){
	// RecoveredElement current = this;
	// while (current != null){
	// if (current instanceof RecoveredInitializer){
	// return (RecoveredInitializer) current;
	// }
	// current = current.parent;
	// }
	// return null;
	// }
	/*
	 * Answer the enclosing method node, or null if none
	 */
	// public RecoveredMethod enclosingMethod(){
	// RecoveredElement current = this;
	// while (current != null){
	// if (current instanceof RecoveredMethod){
	// return (RecoveredMethod) current;
	// }
	// current = current.parent;
	// }
	// return null;
	// }
	/*
	 * Answer the enclosing type node, or null if none
	 */
	// public RecoveredType enclosingType(){
	// RecoveredElement current = this;
	// while (current != null){
	// if (current instanceof RecoveredType){
	// return (RecoveredType) current;
	// }
	// current = current.parent;
	// }
	// return null;
	// }
	/*
	 * Answer the closest specified parser
	 */
	public Parser parser() {
		RecoveredElement current = this;
		while (current != null) {
			if (current.recoveringParser != null) {
				return current.recoveringParser;
			}
			current = current.parent;
		}
		return null;
	}

	/*
	 * Answer the associated parsed structure
	 */
	public ASTNode parseTree() {
		return null;
	}

	/*
	 * Iterate the enclosing blocks and tag them so as to preserve their content
	 */
	// public void preserveEnclosingBlocks(){
	// RecoveredElement current = this;
	// while (current != null){
	// if (current instanceof RecoveredBlock){
	// ((RecoveredBlock)current).preserveContent = true;
	// }
	// if (current instanceof RecoveredType){ // for anonymous types
	// ((RecoveredType)current).preserveContent = true;
	// }
	// current = current.parent;
	// }
	// }
	/*
	 * Answer the position of the previous line end if there is nothing but
	 * spaces in between it and the line end. Used to trim spaces on unclosed
	 * elements.
	 */
	public int previousAvailableLineEnd(int position) {

		Parser parser = this.parser();
		if (parser == null)
			return position;

		Scanner scanner = parser.scanner;
		if (scanner.lineEnds == null)
			return position;

		int index = scanner.getLineNumber(position);
		if (index < 2)
			return position;
		int previousLineEnd = scanner.lineEnds[index - 2];

		char[] source = scanner.source;
		for (int i = previousLineEnd + 1; i < position; i++) {
			if (!(source[i] == ' ' || source[i] == '\t'))
				return position;
		}
		return previousLineEnd;
	}

	/*
	 * Answer the very source end of the corresponding parse node
	 */
	public int sourceEnd() {
		return 0;
	}

	protected String tabString(int tab) {
		StringBuffer result = new StringBuffer();
		for (int i = tab; i > 0; i--) {
			result.append("  "); //$NON-NLS-1$
		}
		return result.toString();
	}

	/*
	 * Answer the top node
	 */
	public RecoveredElement topElement() {
		RecoveredElement current = this;
		while (current.parent != null) {
			current = current.parent;
		}
		return current;
	}

	public String toString() {
		return toString(0);
	}

	public String toString(int tab) {
		return super.toString();
	}

	/*
	 * Answer the enclosing type node, or null if none
	 */
	// public RecoveredType type(){
	// RecoveredElement current = this;
	// while (current != null){
	// if (current instanceof RecoveredType){
	// return (RecoveredType) current;
	// }
	// current = current.parent;
	// }
	// return null;
	// }
	/*
	 * Update the bodyStart of the corresponding parse node
	 */
	public void updateBodyStart(int bodyStart) {
		this.foundOpeningBrace = true;
	}

	/*
	 * Update the corresponding parse node from parser state which is about to
	 * disappear because of restarting recovery
	 */
	public void updateFromParserState() {
	}

	/*
	 * A closing brace got consumed, might have closed the current element, in
	 * which case both the currentElement is exited
	 */
	public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd) {
		if ((--bracketBalance <= 0) && (parent != null)) {
			this.updateSourceEndIfNecessary(braceEnd);
			return parent;
		}
		return this;
	}

	/*
	 * An opening brace got consumed, might be the expected opening one of the
	 * current element, in which case the bodyStart is updated.
	 */
	public RecoveredElement updateOnOpeningBrace(int braceEnd) {

		if (bracketBalance++ == 0) {
			this.updateBodyStart(braceEnd + 1);
			return this;
		}
		return null; // no update is necessary
	}

	/*
	 * Final update the corresponding parse node
	 */
	public void updateParseTree() {
	}

	/*
	 * Update the declarationSourceEnd of the corresponding parse node
	 */
	public void updateSourceEndIfNecessary(int sourceEnd) {
	}
}
