using CanonSharp.Pascal.Parser;
using CanonSharp.Pascal.SyntaxTree;
using CanonSharp.Tests.Utils;

namespace CanonSharp.Tests.ParserTests;

public class StatementParserTests : GrammarParserTestBase
{
    [Fact]
    public void StatementTest1()
    {
        AssignNode node = RunParser<AssignNode>(GrammarParser.StatementParser(), "temp := 1");

        Assert.Equal("temp", node.Variable.Identifier.LiteralValue);
        Assert.Equal(1, node.Expression.Convert<IntegerValueNode>().Value);
    }

    [Fact]
    public void IfTest1()
    {
        IfNode node = RunParser<IfNode>(GrammarParser.IfParser(), """
                                                                  if i = 1 then
                                                                    a := a + 1
                                                                  else
                                                                    a := a + 2
                                                                  """);
        Assert.NotNull(node.ElseStatement);

        BinaryOperatorNode condition = node.Condition.Convert<BinaryOperatorNode>();
        Assert.Equal(BinaryOperatorType.Equal, condition.OperatorType);

        AssignNode statement = node.Statement.Convert<AssignNode>();
        Assert.Equal("a", statement.Variable.Identifier.LiteralValue);

        AssignNode elseStatement = node.Statement.Convert<AssignNode>();
        Assert.Equal("a", elseStatement.Variable.Identifier.LiteralValue);
    }

    [Fact]
    public void IfTest2()
    {
        IfNode node = RunParser<IfNode>(GrammarParser.IfParser(), """
                                                                  if i = 1 then
                                                                    if i = 2 then
                                                                        a := a + 1
                                                                    else
                                                                        a := a + 2
                                                                  """);

        Assert.Null(node.ElseStatement);

        IfNode subIfNode = node.Statement.Convert<IfNode>();
        Assert.NotNull(subIfNode.ElseStatement);
    }

    [Fact]
    public void ForTest1()
    {
        ForNode node = RunParser<ForNode>(GrammarParser.ForParser(), """
                                                                     for i := 1 to 10 do
                                                                        a := a + i
                                                                     """);
        Assert.Equal("i", node.Identifier.LiteralValue);
        Assert.Equal(1, node.LeftCondition.Convert<IntegerValueNode>().Value);
        Assert.Equal(10, node.RightCondition.Convert<IntegerValueNode>().Value);

        AssignNode assignNode = node.Statement.Convert<AssignNode>();
        Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue);
    }

    [Fact]
    public void WhileTest1()
    {
        WhileNode node = RunParser<WhileNode>(GrammarParser.WhileParser(), """
                                                                           while c >= 1 do
                                                                            a := a + 1;
                                                                           """);

        BinaryOperatorNode binaryOperatorNode = node.Condition.Convert<BinaryOperatorNode>();
        Assert.Equal(BinaryOperatorType.GreaterEqual, binaryOperatorNode.OperatorType);

        AssignNode assignNode = node.Statement.Convert<AssignNode>();
        Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue);
    }

    [Theory]
    [InlineData("""
                begin
                temp := 1 + 1;
                flag := true and false;
                end
                """)]
    [InlineData("""
                begin
                temp := 1 + 1;
                flag := true and false
                end
                """)]
    public void CompoundStatementTest1(string input)
    {
        BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), input);

        Assert.Equal(2, node.Statements.Count);
        AssignNode assignNode = node.Statements[0].Convert<AssignNode>();
        Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);

        assignNode = node.Statements[1].Convert<AssignNode>();
        Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue);
    }

    [Fact]
    public void CompoundStatementTest2()
    {
        BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), "begin end");
        Assert.Empty(node.Statements);
    }

    [Fact]
    public void ProgramHeadTest1()
    {
        ProgramHead head = RunParser<ProgramHead>(GrammarParser.ProgramHeadParser(), "program main");

        Assert.Equal("main", head.ProgramName.LiteralValue);
    }

    [Theory]
    [InlineData("""
                begin
                temp := 1 + 1;
                flag := true and false;
                end
                """)]
    [InlineData("""
                begin
                temp := 1 + 1;
                flag := true and false
                end
                """)]
    public void ProgramBodyTest1(string input)
    {
        BlockNode node = RunParser<ProgramBody>(GrammarParser.ProgramBodyParser(), input).MainBlock;

        Assert.Equal(2, node.Statements.Count);
        AssignNode assignNode = node.Statements[0].Convert<AssignNode>();
        Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);

        assignNode = node.Statements[1].Convert<AssignNode>();
        Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue);
    }
}