package at.ac.tuwien.kr.alpha.commons.programs.rules.heads;

import static at.ac.tuwien.kr.alpha.commons.util.Util.join;

import java.util.List;

import at.ac.tuwien.kr.alpha.api.ComparisonOperator;
import at.ac.tuwien.kr.alpha.api.programs.atoms.BasicAtom;
import at.ac.tuwien.kr.alpha.api.programs.literals.Literal;
import at.ac.tuwien.kr.alpha.api.programs.rules.heads.ChoiceHead;
import at.ac.tuwien.kr.alpha.api.programs.terms.Term;

/**
 * Represents the head of a choice rule.
 * 
 * Copyright (c) 2017-2021, the Alpha Team.
 */
class ChoiceHeadImpl implements ChoiceHead {
	
	private final List<ChoiceElement> choiceElements;

	private final Term lowerBound;
	private final ComparisonOperator lowerOp;

	private final Term upperBound;
	private final ComparisonOperator upperOp;

	static class ChoiceElementImpl implements ChoiceElement {
		public final BasicAtom choiceAtom;
		public final List<Literal> conditionLiterals;

		public ChoiceElementImpl(BasicAtom choiceAtom, List<Literal> conditionLiterals) {
			this.choiceAtom = choiceAtom;
			this.conditionLiterals = conditionLiterals;
		}

		@Override
		public String toString() {
			String result = choiceAtom.toString();

			if (conditionLiterals == null || conditionLiterals.size() == 0) {
				return result;
			}

			return join(result + " : ", conditionLiterals, "");
		}

		@Override
		public BasicAtom getChoiceAtom() {
			return choiceAtom;
		}

		@Override
		public List<Literal> getConditionLiterals() {
			return conditionLiterals;
		}
	}

	@Override
	public ComparisonOperator getLowerOperator() {
		return lowerOp;
	}

	@Override
	public ComparisonOperator getUpperOperator() {
		return upperOp;
	}

	@Override
	public List<ChoiceElement> getChoiceElements() {
		return choiceElements;
	}

	@Override
	public Term getLowerBound() {
		return lowerBound;
	}

	@Override
	public Term getUpperBound() {
		return upperBound;
	}

	ChoiceHeadImpl(List<ChoiceElement> choiceElements, Term lowerBound, ComparisonOperator lowerOp, Term upperBound,
			ComparisonOperator upperOp) {
		this.choiceElements = choiceElements;
		this.lowerBound = lowerBound;
		this.lowerOp = lowerOp;
		this.upperBound = upperBound;
		this.upperOp = upperOp;
	}

	@Override
	public String toString() {
		String result = "";

		if (lowerBound != null) {
			result += lowerBound.toString() + lowerOp;
		}

		result += join("{ ", choiceElements, "; ", " }");

		if (upperBound != null) {
			result += upperOp.toString() + upperBound;
		}

		return result;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((this.choiceElements == null) ? 0 : this.choiceElements.hashCode());
		result = prime * result + ((this.lowerBound == null) ? 0 : this.lowerBound.hashCode());
		result = prime * result + ((this.lowerOp == null) ? 0 : this.lowerOp.hashCode());
		result = prime * result + ((this.upperBound == null) ? 0 : this.upperBound.hashCode());
		result = prime * result + ((this.upperOp == null) ? 0 : this.upperOp.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof ChoiceHeadImpl)) {
			return false;
		}
		ChoiceHeadImpl other = (ChoiceHeadImpl) obj;
		if (this.choiceElements == null) {
			if (other.choiceElements != null) {
				return false;
			}
		} else if (!this.choiceElements.equals(other.choiceElements)) {
			return false;
		}
		if (this.lowerBound == null) {
			if (other.lowerBound != null) {
				return false;
			}
		} else if (!this.lowerBound.equals(other.lowerBound)) {
			return false;
		}
		if (this.lowerOp != other.lowerOp) {
			return false;
		}
		if (this.upperBound == null) {
			if (other.upperBound != null) {
				return false;
			}
		} else if (!this.upperBound.equals(other.upperBound)) {
			return false;
		}
		if (this.upperOp != other.upperOp) {
			return false;
		}
		return true;
	}

}
