package prolog.builtins

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import prolog.ast.Database.Program
import prolog.ast.arithmetic.Integer
import prolog.ast.lists.List.Cons
import prolog.ast.lists.List.Empty
import prolog.ast.logic.Rule
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable

class DelimitedContinuationsOperatorsTests {
    @BeforeEach
    fun setup() {
        Program.reset()
    }

    @Test
    fun `example member`() {
        val member = Member(Variable("X"), Cons(Integer(1), Cons(Integer(2), Cons(Integer(3), Empty))))
        val reset = Reset(member, Variable("Ball"), Variable("Cont"))

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

        assertEquals(3, result.size, "Expected 3 results")

        assertTrue(result[0].isSuccess, "Expected success for first result")
        val subs0 = result[0].getOrNull()!!
        assertEquals(2, subs0.size, "Expected 2 substitutions for first result")
        assertEquals(Integer(1), subs0[Variable("X")])
        assertEquals(Integer(0), subs0[Variable("Cont")])

        assertTrue(result[1].isSuccess, "Expected success for second result")
        val subs1 = result[1].getOrNull()!!
        assertEquals(2, subs1.size, "Expected 2 substitutions for second result")
        assertEquals(Integer(2), subs1[Variable("X")])
        assertEquals(Integer(0), subs1[Variable("Cont")])

        assertTrue(result[2].isSuccess, "Expected success for third result")
        val subs2 = result[2].getOrNull()!!
        assertEquals(2, subs2.size, "Expected 2 substitutions for third result")
        assertEquals(Integer(3), subs2[Variable("X")])
        assertEquals(Integer(0), subs2[Variable("Cont")])
    }

    @Test
    fun `example with output`() {
        val reset = Reset(Atom("test_"), Variable("Term"), Variable("Cont"))
        val isOp = Is(Variable("X"), Add(Integer(1), Multiply(Integer(2), Variable("Y"))))
        Program.load(
            listOf(
                Rule(
                    Structure(Atom("test"), listOf(Variable("Cont"), Variable("Term"))),
                    Conjunction(WriteLn("Inside test"), Conjunction(reset, WriteLn("After reset")))
                ),
                Rule(
                    Atom("test_"),
                    Conjunction(WriteLn("Entering reset"), Conjunction(Shift(Variable("Y")), Conjunction(isOp, Conjunction(Write("In test X = "), Conjunction(Write(Variable("X")), WriteLn("; done"))))))
                )
            )
        )

        val notNot2 = Not(Not(Conjunction(WriteLn("calling Cont(2)"), Conjunction(Unify(Variable("Term"), Integer(2)), Call(Variable("Cont"))))))
        val notNot4 = Not(Not(Conjunction(WriteLn("calling Cont(4)"), Conjunction(Unify(Variable("Term"), Integer(4)), Call(Variable("Cont"))))))
        val query = Conjunction(Structure(Atom("test"), listOf(Variable("Cont"), Variable("Term"))), Conjunction(notNot2, notNot4))

        val result = query.satisfy(emptyMap()).toList()
    }
}
