package cd.transform.analysis;


public class MaybeC<E> {
	private E value;
	private boolean anyValue;

	static <T> MaybeC<T> anyValue() {
		return new MaybeC<>(null, true);
	}

	static <T> MaybeC<T> notConstant() {
		return new MaybeC<>(null, false);
	}

	static <T> MaybeC<T> value(T value) {
		return new MaybeC<>(value, false);
	}

	private MaybeC(E value, boolean anyValue) {
		this.value = value;
		this.anyValue = anyValue;
	}

	MaybeC(MaybeC<E> base) {
		this.value = base.value;
		this.anyValue = base.anyValue;
	}

	public void setValue(E value) {
		assert isAnyValue() || isConstant();
		assert value != null;
		anyValue = false;
		this.value = value;
	}

	public E getValue() {
		assert isConstant();
		return value;
	}

	public boolean isAnyValue() {
		return anyValue && value == null;
	}

	public boolean isNotConstant() {
		return !anyValue && value == null;
	}

	public boolean isConstant() {
		return !anyValue && value != null;
	}

	public void setNotConstant() {
		anyValue = false;
		value = null;
	}

	public void setAnyValue() {
		assert isAnyValue();
	}

	/**
	 * Updates the current object, following the table: <br/>
	 * <table border="1">
	 * <tr><td>		</td><td>notConstant</td><td>v1</td><td>anyValue</td></tr>
	 * <tr><td>notConstant</td><td>notConstant</td><td>notConstant</td><td>notConstant</td></tr>
	 * <tr><td>v2</td><td>notConstant</td><td>v1==v2?v1:notConstant</td><td>v2</td></tr>
	 * <tr><td>anyValue</td><td>notConstant</td><td>v1</td><td>anyValue</td></tr>
	 * </table>
	 */
	public void joinWith(MaybeC<E> other) {
		if (this.isNotConstant() || other.isNotConstant()) {
			setNotConstant();
		} else if (!this.isAnyValue() || !other.isAnyValue()) {
			if (this.isAnyValue())
				setValue(other.getValue()); // Copy the other's value
			else if (!other.isAnyValue() && this.getValue() != other.getValue())
				setNotConstant(); // Values don't match
		}
	}


	@Override
	public boolean equals(Object obj) {
		if (obj instanceof MaybeC) {
			MaybeC other = (MaybeC) obj;
			if (isNotConstant() && other.isNotConstant())
				return true;
			if (isAnyValue() && other.isAnyValue())
				return true;
			if (isConstant() && other.isConstant()) {
				if (value == null || other.value == null) {
					return value == other.value;
				} else {
					return value.equals(other.value);
				}
			}
		}
		return false;
	}


	@Override
	public String toString() {
		if (isAnyValue()) return "any";
		if (isNotConstant()) return "none";
		if (isConstant()) return getValue().toString();
		throw new RuntimeException("MaybeC in erroneous state");
	}
}

