/*******************************************************************************
 * Copyright (c) 2000, 2004 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.flow;
//import checkers.inference.ownership.quals.*;

import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;

public abstract class FlowInfo {

	public final static int REACHABLE = 0;
	public final static int UNREACHABLE = 1;
	
	public final static int UNKNOWN = 0;
	public final static int NULL = 1;
	public final static int NON_NULL = -1;
	
	public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization
	static {
		DEAD_END = new UnconditionalFlowInfo();
		DEAD_END.reachMode = UNREACHABLE;
	}
	abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits);

	abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits);

	public FlowInfo asNegatedCondition() {

		return (/*@OwnPar*/ /*@NoRep*/ FlowInfo)this;
	}

	public static FlowInfo conditional(FlowInfo initsWhenTrue, FlowInfo initsWhenFalse){

		// if (initsWhenTrue.equals(initsWhenFalse)) return initsWhenTrue; -- could optimize if #equals is defined
		return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse);
	}

	abstract public FlowInfo copy();

	public static UnconditionalFlowInfo initial(int maxFieldCount) {
		UnconditionalFlowInfo info = new UnconditionalFlowInfo();
		info.maxFieldCount = maxFieldCount;
		return info;
	}

	abstract public FlowInfo initsWhenFalse();

	abstract public FlowInfo initsWhenTrue();

	/**
	 * Check status of definite assignment for a field.
	 */
	 abstract public boolean isDefinitelyAssigned(FieldBinding field);   

	/**
	 * Check status of definite assignment for a local.
	 */
	public abstract boolean isDefinitelyAssigned(LocalVariableBinding local);

	/**
	 * Check status of definite null assignment for a field.
	 */
	 abstract public boolean isDefinitelyNonNull(FieldBinding field);   

	/**
	 * Check status of definite null assignment for a local.
	 */
	public abstract boolean isDefinitelyNonNull(LocalVariableBinding local);

	/**
	 * Check status of definite null assignment for a field.
	 */
	 abstract public boolean isDefinitelyNull(FieldBinding field);   

	/**
	 * Check status of definite null assignment for a local.
	 */
	public abstract boolean isDefinitelyNull(LocalVariableBinding local);

	/**
	 * Check status of potential assignment for a field.
	 */
	 abstract public boolean isPotentiallyAssigned(FieldBinding field);   

	/**
	 * Check status of potential assignment for a local variable.
	 */

	 abstract public boolean isPotentiallyAssigned(LocalVariableBinding field);   

	abstract public boolean isReachable();
	
	/**
	 * Record a field got definitely assigned.
	 */
	abstract public void markAsDefinitelyAssigned(FieldBinding field);

	/**
	 * Record a local got definitely assigned to a non-null value.
	 */
	abstract public void markAsDefinitelyNonNull(LocalVariableBinding local);

	/**
	 * Record a field got definitely assigned to a non-null value.
	 */
	abstract public void markAsDefinitelyNonNull(FieldBinding field);

	/**
	 * Record a local got definitely assigned to null.
	 */
	abstract public void markAsDefinitelyNull(LocalVariableBinding local);

	/**
	 * Record a field got definitely assigned.
	 */
	abstract public void markAsDefinitelyNull(FieldBinding field);

	/**
	 * Record a local got definitely assigned.
	 */
	abstract public void markAsDefinitelyAssigned(LocalVariableBinding local);

	/**
	 * Clear the initialization info for a field
	 */
	abstract public void markAsDefinitelyNotAssigned(FieldBinding field);

	/**
	 * Clear the initialization info for a local variable
	 */
	abstract public void markAsDefinitelyNotAssigned(LocalVariableBinding local);

	/**
	 * Merge branches using optimized boolean conditions
	 */
	public static FlowInfo mergedOptimizedBranches(FlowInfo initsWhenTrue, boolean isOptimizedTrue, FlowInfo initsWhenFalse, boolean isOptimizedFalse, boolean allowFakeDeadBranch) {
		FlowInfo mergedInfo;
		if (isOptimizedTrue){
			if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) {
				mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
			} else {
				mergedInfo = initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse);
			}

		} else if (isOptimizedFalse) {
			if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
				mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
			} else {
				mergedInfo = initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue);
			}

		} else {
			mergedInfo = initsWhenTrue.unconditionalInits().mergedWith(initsWhenFalse.unconditionalInits());
		}
		return mergedInfo;
	}
	
	abstract public int reachMode();

	abstract public FlowInfo setReachMode(int reachMode);

	/**
	 * Returns the receiver updated in the following way: <ul>
	 * <li> intersection of definitely assigned variables, 
	 * <li> union of potentially assigned variables.
	 * </ul>
	 */
	abstract public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits);

	public String toString(){

		if (this == DEAD_END){
			return "FlowInfo.DEAD_END"; //$NON-NLS-1$
		}
		return super.toString();
	}

	abstract public UnconditionalFlowInfo unconditionalInits();
}
