package e2e

import interpreter.FileLoader
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import prolog.ast.Database.Program
import java.io.ByteArrayOutputStream
import java.io.PrintStream

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Examples {
    private val loader = FileLoader()
    private lateinit var outStream: ByteArrayOutputStream

    @BeforeEach
    fun setup() {
        Program.reset()

        outStream = ByteArrayOutputStream()
        System.setOut(PrintStream(outStream))
    }

    @Test
    fun debugHelper() {
        loader.load("examples/meta/mib_voorbeelden.pl")
    }

    @ParameterizedTest
    @MethodSource("basics")
    fun `Identical output for basics`(inputFile: String, expected: String) {
        loader.load("examples/basics/$inputFile")
        assertEquals(expected, outStream.toString())
    }

    @ParameterizedTest
    @MethodSource("meta")
    fun `Identical output for meta`(inputFile: String, expected: String) {
        loader.load("examples/meta/$inputFile")
        assertEquals(expected, outStream.toString())
    }

    @ParameterizedTest
    @MethodSource("other")
    fun `Identical output for other`(inputFile: String, expected: String) {
        loader.load("examples/$inputFile")
        assertEquals(expected, outStream.toString())
    }

    fun basics() = listOf(
        Arguments.of("arithmetics.pl", "gimli is a level 4 fighter with 35 hitpoints.\nlegolas is a level 5 ranger with 30 hitpoints.\ngandalf is a level 10 wizard with 25 hitpoints.\nfrodo is a level 2 rogue with 20 hitpoints.\nlegolas threw gimli, and gimli took 5 damage.\ngimli is a level 4 fighter with 30 hitpoints.\ngandalf casts aid.\ngimli is a level 4 fighter with 35 hitpoints.\nlegolas leveled up.\nlegolas is a level 6 ranger with 30 hitpoints.\n"),
        Arguments.of("backtracking.pl", "0\ns(0)\ns(s(0))\ns(s(s(0)))\n"),
        Arguments.of("cut.pl", "0\n"),
        Arguments.of("disjunction.pl", "Alice likes Italian food.\nBob likes Italian food.\n"),
        Arguments.of("equality.pl", "X == Y failed\nX = Y succeeded\nX == Y succeeded\nX = Y succeeded\nX == Y succeeded\n"),
        Arguments.of("forall.pl", "Only alice likes pizza.\n"),
        Arguments.of("fraternity.pl", "Citizen robespierre is eligible for the event.\nCitizen danton is eligible for the event.\nCitizen camus is eligible for the event.\n"),
        Arguments.of("liberty.pl", "Give me Liberty, or give me Death!\nI disapprove of what you say, but I will defend to the death your right to say it.\nThe revolution devours its own children.\nSo this is how liberty dies, with thunderous applause.\n"),
        Arguments.of("summer.pl", "The sum of 1 to 9 is: 36\n"),
        Arguments.of("unification.pl", "While alice got an A, carol got an A, but bob did not get an A, dave did not get an A, unfortunately.\n"),
        Arguments.of("write.pl", "gpl zegt: dag(wereld)\n"),
    )

    fun meta() = listOf(
        Arguments.of("ceremony.pl", "/Ceremony starts/\nGood evening everyone!\nThanks for coming John, Mary, and my parents!\n/Ceremony ends/\n"),
        Arguments.of("continuations.pl", "Inside test\nEntering reset\nAfter reset\nCalling Cont(2)\nIn test X = 5; done\nCalling Cont(4)\nIn test X = 9; done\n"),
        Arguments.of("mib_voorbeelden.pl", "b\nf(b)\nf(g(a,a),h(c,d),i(e,f))\nf(g(a,a),h(c,c),i(e,f))\nf(g(a,a),h(c,c),i(e,e))\n")
    )

    fun other() = listOf(
        Arguments.of("program.pl", "10\nhello(world)")
    )
}
