/*******************************************************************************
 * 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.flow.FlowContext;
import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
import net.sourceforge.phpdt.internal.compiler.impl.Constant;
import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;

public class IfStatement extends Statement {

	// this class represents the case of only one statement in
	// either else and/or then branches.

	public Expression condition;

	public Statement thenStatement;

	public Statement elseStatement;

	public Expression[] elseifConditions;

	public Statement[] elseifStatements;

	public boolean checkUnreachable;

	boolean thenExit;

	// for local variables table attributes
	int thenInitStateIndex = -1;

	int elseInitStateIndex = -1;

	int mergedInitStateIndex = -1;

	public IfStatement(Expression condition, Statement thenStatement, int s,
			int e) {

		this.condition = condition;
		this.thenStatement = thenStatement;
		sourceStart = s;
		sourceEnd = e;
		checkUnreachable = true;
	}

	public IfStatement(Expression condition, Statement thenStatement,
			Statement elseStatement, int s, int e) {

		this.condition = condition;
		this.thenStatement = thenStatement;
		this.elseStatement = elseStatement;
		sourceEnd = e;
		sourceStart = s;
		checkUnreachable = true;
	}

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

		// process the condition
		flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo);

		Constant cst = this.condition.optimizedBooleanConstant();
		boolean isConditionOptimizedTrue = cst != NotAConstant
				&& cst.booleanValue() == true;
		boolean isConditionOptimizedFalse = cst != NotAConstant
				&& cst.booleanValue() == false;

		// process the THEN part
		FlowInfo thenFlowInfo = flowInfo.initsWhenTrue().copy();
		if (isConditionOptimizedFalse) {
			thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
		}
		if (this.thenStatement != null) {
			// Save info for code gen
			thenInitStateIndex = currentScope.methodScope()
					.recordInitializationStates(thenFlowInfo);
			if (!thenFlowInfo.complainIfUnreachable(thenStatement,
					currentScope, false)) {
				thenFlowInfo = thenStatement.analyseCode(currentScope,
						flowContext, thenFlowInfo);
			}
		}
		;
		// optimizing the jump around the ELSE part
		this.thenExit = !thenFlowInfo.isReachable();

		// process the ELSE part
		FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy();
		if (isConditionOptimizedTrue) {
			elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
		}
		if (this.elseStatement != null) {
			// Save info for code gen
			elseInitStateIndex = currentScope.methodScope()
					.recordInitializationStates(elseFlowInfo);
			if (!elseFlowInfo.complainIfUnreachable(elseStatement,
					currentScope, false)) {
				elseFlowInfo = elseStatement.analyseCode(currentScope,
						flowContext, elseFlowInfo);
			}
		}

		boolean elseExit = !elseFlowInfo.isReachable();

		// merge THEN & ELSE initializations
		FlowInfo mergedInfo;
		// if (isConditionOptimizedTrue){
		// if (!this.thenExit) {
		// mergedInfo = thenFlowInfo;
		// } else {
		// mergedInfo = elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
		// }
		//
		// } else if (isConditionOptimizedFalse) {
		// if (!elseExit) {
		// mergedInfo = elseFlowInfo;
		// } else {
		// mergedInfo = thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
		// }
		//
		// } else {
		// mergedInfo =
		// thenFlowInfo.mergedWith(elseFlowInfo.unconditionalInits());
		// }
		if (isConditionOptimizedTrue) {
			if (!this.thenExit) {
				mergedInfo = thenFlowInfo
						.addPotentialInitializationsFrom(elseFlowInfo);
			} else {
				mergedInfo = elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
			}

		} else if (isConditionOptimizedFalse) {
			if (!elseExit) {
				mergedInfo = elseFlowInfo
						.addPotentialInitializationsFrom(thenFlowInfo);
			} else {
				mergedInfo = thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
			}

		} else {
			mergedInfo = thenFlowInfo.mergedWith(elseFlowInfo
					.unconditionalInits());
		}

		mergedInitStateIndex = currentScope.methodScope()
				.recordInitializationStates(mergedInfo);
		return mergedInfo;
	}

	/**
	 * If code generation
	 * 
	 * @param currentScope
	 *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
	 * @param codeStream
	 *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
	 */
	// public void generateCode(BlockScope currentScope, CodeStream codeStream)
	// {
	//
	// if ((this.bits & IsReachableMASK) == 0) {
	// return;
	// }
	// int pc = codeStream.position;
	// Label endifLabel = new Label(codeStream);
	//
	// // optimizing the then/else part code gen
	// Constant cst;
	// boolean hasThenPart =
	// !(((cst = this.condition.optimizedBooleanConstant()) != NotAConstant
	// && cst.booleanValue() == false)
	// || this.thenStatement == null
	// || this.thenStatement.isEmptyBlock());
	// boolean hasElsePart =
	// !((cst != NotAConstant && cst.booleanValue() == true)
	// || this.elseStatement == null
	// || this.elseStatement.isEmptyBlock());
	//
	// if (hasThenPart) {
	// Label falseLabel;
	// // generate boolean condition
	// this.condition.generateOptimizedBoolean(
	// currentScope,
	// codeStream,
	// null,
	// (falseLabel = new Label(codeStream)),
	// true);
	// // May loose some local variable initializations : affecting the local
	// variable attributes
	// if (thenInitStateIndex != -1) {
	// codeStream.removeNotDefinitelyAssignedVariables(
	// currentScope,
	// thenInitStateIndex);
	// codeStream.addDefinitelyAssignedVariables(currentScope,
	// thenInitStateIndex);
	// }
	// // generate then statement
	// this.thenStatement.generateCode(currentScope, codeStream);
	// // jump around the else statement
	// if (hasElsePart && !thenExit) {
	// this.thenStatement.branchChainTo(endifLabel);
	// int position = codeStream.position;
	// codeStream.goto_(endifLabel);
	// codeStream.updateLastRecordedEndPC(position);
	// //goto is tagged as part of the thenAction block
	// }
	// falseLabel.place();
	// } else {
	// if (hasElsePart) {
	// // generate boolean condition
	// this.condition.generateOptimizedBoolean(
	// currentScope,
	// codeStream,
	// endifLabel,
	// null,
	// true);
	// } else {
	// // generate condition side-effects
	// this.condition.generateCode(currentScope, codeStream, false);
	// codeStream.recordPositionsFrom(pc, this.sourceStart);
	// }
	// }
	// // generate else statement
	// if (hasElsePart) {
	// // May loose some local variable initializations : affecting the local
	// variable attributes
	// if (elseInitStateIndex != -1) {
	// codeStream.removeNotDefinitelyAssignedVariables(
	// currentScope,
	// elseInitStateIndex);
	// codeStream.addDefinitelyAssignedVariables(currentScope,
	// elseInitStateIndex);
	// }
	// this.elseStatement.generateCode(currentScope, codeStream);
	// }
	// endifLabel.place();
	// // May loose some local variable initializations : affecting the local
	// variable attributes
	// if (mergedInitStateIndex != -1) {
	// codeStream.removeNotDefinitelyAssignedVariables(
	// currentScope,
	// mergedInitStateIndex);
	// }
	// codeStream.recordPositionsFrom(pc, this.sourceStart);
	// }
	public StringBuffer printStatement(int indent, StringBuffer output) {

		printIndent(indent, output).append("if ("); //$NON-NLS-1$
		condition.printExpression(0, output).append(")\n"); //$NON-NLS-1$ 
		thenStatement.printStatement(indent + 2, output);
		if (elseStatement != null) {
			output.append('\n');
			printIndent(indent, output);
			output.append("else\n"); //$NON-NLS-1$
			elseStatement.printStatement(indent + 2, output);
		}
		return output;
	}

	public void resolve(BlockScope scope) {

		TypeBinding type = condition
				.resolveTypeExpecting(scope, BooleanBinding);
		condition.implicitWidening(type, type);
		if (thenStatement != null)
			thenStatement.resolve(scope);
		if (elseStatement != null)
			elseStatement.resolve(scope);
	}

	public String toString(int tab) {

		String inFront, s = tabString(tab);
		inFront = s;
		s = s + "if (" + condition.toStringExpression() + ") \n"; //$NON-NLS-1$ //$NON-NLS-2$
		s = s + thenStatement.toString(tab + 2) + ";"; //$NON-NLS-1$
		if (elseStatement != null)
			s = s
					+ "\n" + inFront + "else\n" + elseStatement.toString(tab + 2) + ";"; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-3$
		return s;
	}

	public void traverse(ASTVisitor visitor, BlockScope blockScope) {

		if (visitor.visit(this, blockScope)) {
			condition.traverse(visitor, blockScope);
			if (thenStatement != null)
				thenStatement.traverse(visitor, blockScope);
			if (elseStatement != null)
				elseStatement.traverse(visitor, blockScope);
		}
		visitor.endVisit(this, blockScope);
	}
}
