/**
 * Comparison and Unification of Terms
 *
 * [SWI Prolog Documentation](https://www.swi-prolog.org/pldoc/man?section=compare)
 */
package prolog.builtins

import prolog.Answers
import prolog.Substitutions
import prolog.ast.terms.Operator
import prolog.ast.terms.Term
import prolog.logic.applySubstitution
import prolog.logic.equivalent
import prolog.logic.unifyLazy

/**
 * Unify Term1 with Term2. True if the unification succeeds.
 */
class Unify(private val term1: Term, private val term2: Term): Operator("=", term1, term2) {
    override fun satisfy(subs: Substitutions): Answers = sequence {
        val t1 = applySubstitution(term1, subs)
        val t2 = applySubstitution(term2, subs)

        yieldAll(unifyLazy(t1, t2, emptyMap()))
    }

    override fun applySubstitution(subs: Substitutions): Unify = Unify(
        applySubstitution(term1, subs),
        applySubstitution(term2, subs)
    )
}

class NotUnify(private val term1: Term, private val term2: Term) : Not(Unify(term1, term2)) {
    override fun toString(): String = "($term1 \\= $term2)"
}

class Equivalent(private val term1: Term, private val term2: Term) : Operator("==", term1, term2) {
    override fun satisfy(subs: Substitutions): Answers = sequence {
        val t1 = applySubstitution(term1, subs)
        val t2 = applySubstitution(term2, subs)

        if (equivalent(t1, t2, emptyMap())) {
            yield(Result.success(emptyMap()))
        }
    }

    override fun applySubstitution(subs: Substitutions): Equivalent = Equivalent(
        applySubstitution(term1, subs),
        applySubstitution(term2, subs)
    )
}

class NotEquivalent(private val term1: Term, private val term2: Term) : Not(Equivalent(term1, term2)) {
    override fun toString(): String = "($term1 \\== $term2)"
}
