/* 
 * Kodkod -- Copyright (c) 2005-2011, Emina Torlak
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package kodkod.engine.bool;

import java.util.Iterator;
import java.util.Set;

import kodkod.util.collections.Containers;
import kodkod.util.ints.Ints;

/**
 * An if-then-else gate.
 * 
 * @specfield ifFormula: BooleanFormula
 * @specfield thenFormula: BooleanFormula
 * @specfield elseFormula: BooleanFormula
 * @invariant inputs = 0->ifFormula + 1->thenFormula + 2->elseFormula
 * @invariant this.label > 0
 * @invariant this.op = Operator.ITE
 * @specfield all input: inputs | this.label > |input.label|
 * @author Emina Torlak
 */
public final class ITEGate extends BooleanFormula {
	private final BooleanFormula[] inputs;
	private final int label, hashcode, labelhash;
	
	/**
	 * Constructs a new ITEGate from the given formulas and label.
	 * @requires label >= 0 && null !in ifFormula + thenFormula + elseFormula
	 * @requires hashcode = ITE.hash(ifFormula, thenFormula, elseFormula)
	 * @ensures this.label' = label && this.ifFormula' = ifFormula &&
	 * this.thenFormula' = thenFormula && this.elseFormula' = elseFormula
	 * @throws NullPointerException  owner = null
	 */
	ITEGate(int label, int hashcode, BooleanFormula ifFormula, BooleanFormula thenFormula, BooleanFormula elseFormula) {
		super(null);
		assert label >= 0;
		this.label = label;
		this.labelhash = Ints.superFastHash(label);
		this.hashcode = hashcode;
		this.inputs = new BooleanFormula[3];
		inputs[0] = ifFormula;
		inputs[1] = thenFormula;
		inputs[2] = elseFormula;
	}

	/**
	 * Returns a hash of this.label
	 * @return a hash of this.label
	 */
	@Override
	int hash(Operator op) {
		return labelhash;
	}
	
	/**
	 * Returns an iterator over this.inputs
	 * @return returns an iterator over this.inputs
	 * @see kodkod.engine.bool.BooleanFormula#iterator()
	 */
	@Override
	public Iterator<BooleanFormula> iterator() {
		return Containers.iterate(inputs);
	}
	
	/** 
	 * Returns 3.
	 * @return 2
	 * @see kodkod.engine.bool.BooleanFormula#size()
	 */
	@Override
	public int size() {
		return 3;
	}

	/**
	 * Returns this.label
	 * @return this.label
	 * @see kodkod.engine.bool.BooleanValue#label()
	 */
	@Override
	public int label() {
		return label;
	}

	/**
	 * Passes this value and the given
	 * argument value to the visitor, and returns the resulting value.
	 * @return the value produced by the visitor when visiting this node
	 * with the given argument.
	 * @see kodkod.engine.bool.BooleanFormula#accept(kodkod.engine.bool.BooleanVisitor, Object)
	 */
	@Override
	public <T, A> T accept(BooleanVisitor<T, A> visitor, A arg) {
		return visitor.visit(this, arg);
	}

	/**
	 * Returns a string representation of this ITE gate.
	 * @return a string representation of this ITE gate.
	 */
	public String toString() {
		return "(" + inputs[0] + "?" + inputs[1] + ":" + inputs[2] + ")";
	}

	/**
	 * Returns the hashcode for this if-then-else gate.
	 * @return the hashcode for this gate.
	 */
	public int hashCode() {
		return hashcode;
	}
	/**
	 * Returns Operator.ITE.
	 * @return Operator.ITE
	 */
	@Override
	public kodkod.engine.bool.Operator op() {
		return kodkod.engine.bool.Operator.ITE;
	}

	/**
	 * Returns this.inputs[i].
	 * @return this.inputs[i]
	 * @throws IndexOutOfBoundsException  0 < i || i > 2
	 */
	@Override
	public BooleanFormula input(int i) {
		if (i < 0 || i > 2)
			throw new IndexOutOfBoundsException();
		return inputs[i];
	}
	
	 /**
	 * Returns an integer k' such that 0 < |k'| < k and |k'| is the number of flattening
	 * steps that need to be taken to determine that this circuit has (or does not have)
	 * an input with the given label.
	 * A positive k' indicates that f is found to be an input to this circuit in k' steps.
	 * A negative k' indicates that f is not an input to this circuit, when it is flattened
	 * using at most k steps.  
	 * @requires k > 0
	 * @return this=f => 1 else op=ITE && k>2 && f in this.inputs[int].label => 3 else -3
	 */
	@Override
	int contains(Operator op, int f, int k) {
		assert k > 0;
		if (f==label) return 1;
		else if (op != Operator.ITE || k < 3 || f>label || -f>label) return -1;
		else return (inputs[0].label()==f || inputs[1].label()==f || inputs[2].label()==f) ? 3 : -3;
	}
		
	/**
	 * Flattens this circuit with respect to the given operator into 
	 * the provided set.  
	 * @requires k > 0
	 * @ensures op = Operator.ITE && k> 2 => flat.elts' = flat.elts + this.inputs[ints], 
	 *          flat.elts' = flat.elts + this
	 */
	@Override
	void flatten(Operator op, Set<BooleanFormula> flat, int k) {
		assert k > 0;
		if (op==Operator.ITE && k > 2) {
			flat.add(inputs[0]);
			flat.add(inputs[1]);
			flat.add(inputs[2]);
		} else {
			flat.add(this);
		}
	}	
}
