package at.ac.tuwien.kr.alpha.api.programs.atoms;

import java.util.List;
import java.util.Set;

import at.ac.tuwien.kr.alpha.api.grounder.Substitution;
import at.ac.tuwien.kr.alpha.api.programs.Predicate;
import at.ac.tuwien.kr.alpha.api.programs.literals.Literal;
import at.ac.tuwien.kr.alpha.api.programs.terms.Term;
import at.ac.tuwien.kr.alpha.api.programs.terms.VariableTerm;

/**
 * An Atom is the common super-interface of all representations of ASP atoms used by Alpha.
 */
public interface Atom extends Comparable<Atom> {

	Predicate getPredicate();

	List<Term> getTerms();

	/**
	 * Returns whether this atom is ground, i.e., variable-free.
	 *
	 * @return true iff the terms of this atom contain no {@link VariableTerm}.
	 */
	boolean isGround();

	/**
	 * Creates a non-negated literal containing this atom.
	 */
	default Literal toLiteral() {
		return toLiteral(true);
	}

	/**
	 * Creates a literal containing this atom which will be negated if {@code positive} is {@code false}.
	 *
	 * @param positive the polarity of the resulting literal.
	 * @return a literal that is positive iff the given parameter is true.
	 */
	Literal toLiteral(boolean positive);

	Set<VariableTerm> getOccurringVariables();

	Atom substitute(Substitution substitution); // Introduce parameterized interface Substituable<A extends Atom> to get atom types right?

	Atom renameVariables(String newVariablePrefix);

	Atom withTerms(List<Term> terms);

	@Override
	default int compareTo(Atom o) {
		if (o == null) {
			return 1;
		}

		final List<Term> aTerms = this.getTerms();
		final List<Term> bTerms = o.getTerms();

		if (aTerms.size() != bTerms.size()) {
			return Integer.compare(aTerms.size(), bTerms.size());
		}

		int result = this.getPredicate().compareTo(o.getPredicate());

		if (result != 0) {
			return result;
		}

		for (int i = 0; i < aTerms.size(); i++) {
			result = aTerms.get(i).compareTo(o.getTerms().get(i));
			if (result != 0) {
				return result;
			}
		}

		return 0;
	}
}
