package interpreter

import com.github.h0tk3y.betterParse.grammar.parseToEnd
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Nested
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import parser.grammars.TermsGrammar
import prolog.ast.arithmetic.Float
import prolog.ast.arithmetic.Integer
import prolog.ast.terms.Atom
import prolog.ast.terms.Goal
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.builtins.Is
import prolog.builtins.Subtract

class ParserPreprocessorIntegrationTests {
    @Nested
    class `Arithmetic`() {
        val parser = TermsGrammar()
        val preprocessor = OpenPreprocessor()

        @ParameterizedTest
        @ValueSource(strings = ["-1", "-1.0", "-1.5"])
        fun `can parse negative numbers`(input: String) {
            val number = if (input.contains('.')) {
                Float(input.substring(1).toFloat())
            } else {
                Integer(input.substring(1).toInt())
            }
            val negativeNumber = if (input.contains('.')) {
                Float(input.toFloat())
            } else {
                Integer(input.toInt())
            }

            // Check if parser returns the same result

            val parsed = parser.parseToEnd("X is $input") as Term

            assertEquals(
                Structure("is", Variable("X"), Structure("-",number)),
                parsed
            )

            // Check if preprocessor returns the same result

            val prepped = preprocessor.preprocess(parsed)

            val expected = Is(
                Variable("X"),
                Subtract(Integer(0), number)
            )

            assertEquals(expected, prepped)
            assertEquals(expected.toString(), prepped.toString())

            // Check if evaluation is correct

            val solutions = (prepped as Is).satisfy(emptyMap()).toList()

            assertEquals(1, solutions.size)
            assertEquals(negativeNumber, solutions[0].getOrNull()!![Variable("X")])
        }

        @ParameterizedTest
        @ValueSource(strings = ["X is 1 - 2", "X is 1-2"])
        fun `can add negative numbers`(input: String) {
            val result = parser.parseToEnd(input) as Term

            assertEquals(
                Structure("is", Variable("X"), Structure("-", Integer(1), Integer(2))),
                result
            )

            val prepped = preprocessor.preprocess(result)

            val expected = Is(
                Variable("X"),
                Subtract(Integer(1), Integer(2))
            )

            assertEquals(expected, prepped)
            assertEquals(expected.toString(), prepped.toString())
        }
    }
}