/*******************************************************************************
 * 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.ast;

import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
import net.sourceforge.phpdt.internal.compiler.codegen.Label;
import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;

public class Block extends Statement {

	public Statement[] statements;

	public int explicitDeclarations;

	// the number of explicit declaration , used to create scope
	public BlockScope scope;

	public static final Block None = new Block(0);

	public Block(int explicitDeclarations) {
		this.explicitDeclarations = explicitDeclarations;
	}

	public FlowInfo analyseCode(BlockScope currentScope,
			FlowContext flowContext, FlowInfo flowInfo) {

		// empty block
		if (statements == null)
			return flowInfo;
		boolean didAlreadyComplain = false;
		for (int i = 0, max = statements.length; i < max; i++) {
			Statement stat;
			if (!flowInfo.complainIfUnreachable(stat = statements[i], scope,
					didAlreadyComplain)) {
				flowInfo = stat.analyseCode(scope, flowContext, flowInfo);
			} else {
				didAlreadyComplain = true;
			}
		}
		return flowInfo;
	}

	public static final Block EmptyWith(int sourceStart, int sourceEnd) {

		// return an empty block which position is s and e
		Block bk = new Block(0);
		bk.sourceStart = sourceStart;
		bk.sourceEnd = sourceEnd;
		return bk;
	}

	/**
	 * Code generation for a block
	 */
	// public void generateCode(BlockScope currentScope, CodeStream codeStream)
	// {
	//
	// if ((bits & IsReachableMASK) == 0) {
	// return;
	// }
	// int pc = codeStream.position;
	// if (statements != null) {
	// for (int i = 0, max = statements.length; i < max; i++) {
	// statements[i].generateCode(scope, codeStream);
	// }
	// } // for local variable debug attributes
	// if (scope != currentScope) { // was really associated with its own scope
	// codeStream.exitUserScope(scope);
	// }
	// codeStream.recordPositionsFrom(pc, this.sourceStart);
	// }
	public boolean isEmptyBlock() {

		return statements == null;
	}

	public StringBuffer printBody(int indent, StringBuffer output) {

		if (this.statements == null)
			return output;
		for (int i = 0; i < statements.length; i++) {
			statements[i].printStatement(indent + 1, output);
			output.append('\n');
		}
		return output;
	}

	public StringBuffer printStatement(int indent, StringBuffer output) {

		printIndent(indent, output);
		output.append("{\n"); //$NON-NLS-1$
		printBody(indent, output);
		return printIndent(indent, output).append('}');
	}

	public void resolve(BlockScope upperScope) {

		if (statements != null) {
			scope = explicitDeclarations == 0 ? upperScope : new BlockScope(
					upperScope, explicitDeclarations);
			int i = 0, length = statements.length;
			while (i < length)
				statements[i++].resolve(scope);
		}
	}

	public void resolveUsing(BlockScope givenScope) {

		// this optimized resolve(...) is sent only on none empty blocks
		scope = givenScope;
		if (statements != null) {
			int i = 0, length = statements.length;
			while (i < length)
				statements[i++].resolve(scope);
		}
	}

	public String toString(int tab) {

		String s = tabString(tab);
		if (this.statements == null) {
			s += "{\n"; //$NON-NLS-1$
			s += tabString(tab);
			s += "}"; //$NON-NLS-1$
			return s;
		}
		s += "{\n"; //$NON-NLS-1$
		s += this.toStringStatements(tab);
		s += tabString(tab);
		s += "}"; //$NON-NLS-1$
		return s;
	}

	public String toStringStatements(int tab) {

		if (this.statements == null)
			return ""; //$NON-NLS-1$
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < statements.length; i++) {
			buffer.append(statements[i].toString(tab + 1));
			if (statements[i] instanceof Block) {
				buffer.append("\n"); //$NON-NLS-1$
			} else {
				buffer.append(";\n"); //$NON-NLS-1$
			}
		}
		;
		return buffer.toString();
	}

	public void traverse(ASTVisitor visitor, BlockScope blockScope) {

		if (visitor.visit(this, blockScope)) {
			if (statements != null) {
				int statementLength = statements.length;
				for (int i = 0; i < statementLength; i++)
					statements[i].traverse(visitor, scope);
			}
		}
		visitor.endVisit(this, blockScope);
	}

	/**
	 * Dispatch the call on its last statement.
	 */
	public void branchChainTo(Label label) {
		if (this.statements != null) {
			this.statements[statements.length - 1].branchChainTo(label);
		}
	}

}
