package prolog.logic

import prolog.Substitutions
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Term
import prolog.ast.terms.Variable

/**
 * True if [Term] is bound (i.e., not a variable) and is not compound.
 * Thus, atomic acts as if defined by:
 *
 * atomic(Term) :-
 *         nonvar(Term),
 *         \+ compound(Term).
 */
fun atomic(term: Term, subs: Substitutions = emptyMap()): Boolean = nonvariable(term, subs) && !compound(term, subs)

/**
 * True if [Term] is bound to a compound term.
 * See also functor/3 =../2, compound_name_arity/3 and compound_name_arguments/3.
 */
fun compound(term: Term, subs: Substitutions = emptyMap()): Boolean {
    val isCompound = term is CompoundTerm
    val isVariableCompound = term is Variable && term in subs && compound(subs[term]!!, subs)
    return isCompound || isVariableCompound
}

/**
 * True if [Term] currently is not a free variable.
 */
fun nonvariable(term: Term, subs: Substitutions = emptyMap()): Boolean = !variable(term, subs)

/**
 * True if [Term] currently is a free variable.
 */
fun variable(term: Term, subs: Substitutions = emptyMap()): Boolean {
    if (term is Variable) {
        return term !in subs || subs[term] === term || variable(subs[term]!!, subs)
    }

    return false
}
