package prolog.builtins

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import prolog.ast.Database.Program
import prolog.ast.arithmetic.Integer
import prolog.ast.logic.Rule
import prolog.ast.terms.Atom
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Variable
import java.io.ByteArrayOutputStream
import java.io.PrintStream

class OtherOperatorsTests {
    @Test
    fun `forall(X is 1, X == 1)`() {
        val forall = ForAll(Is(Variable("X"), Integer(1)), EvaluatesTo(Variable("X"), Integer(1)))

        val result = forall.satisfy(emptyMap()).toList()

        assertEquals(1, result.size)
    }

    /**
     * @see [Forall instead of failure-driven loops](https://riptutorial.com/prolog/example/19554/forall-instead-of-failure-driven-loops#example)
     */
    @Test
    fun `forall printer`() {
        val printer = Rule(
            CompoundTerm(Atom("print"), listOf(Variable("X"))),
            ForAll(
                Between(Integer(1), Variable("X"), Variable("Y")),
                Write(Variable("Y"))
            )
        )
        Program.load(listOf(printer))

        // Set output
        val outStream = ByteArrayOutputStream()
        System.setOut(PrintStream(outStream))

        var expected = ""
        for (i in 1..5) {
            val result = CompoundTerm(Atom("print"), listOf(Integer(i))).satisfy(emptyMap()).toList()
            assertEquals(1, result.size)
            assertTrue(result[0].isSuccess)

            expected += "$i"
            assertEquals(expected, outStream.toString())
            outStream.reset()
        }
    }
}
