﻿using ExpressionEvaluator.Parsers.Lexing;
using ExpressionEvaluator.Parsers.Lexing.Tokens;
using ExpressionEvaluator.Parsers.Lexing.Tokens.Operators;
using Xunit;

namespace ExpressionEvaluator.UnitTests.Lexing
{
    [Trait("Category", "Lexer")]
    public class TokenizerTest
    {
        [Fact]
        public void TestNumber5Plus()
        {
            const string input = "5+";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(2, tokens.Count);
            Assert.IsType<NumberToken>(tokens[0]);
            Assert.Equal(1, tokens[0].Lexeme.Length);
            Assert.IsType<OperatorPlusToken>(tokens[1]);
        }

        [Fact]
        public void TestNumber5Plus5()
        {
            const string input = "5+5";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(3, tokens.Count);
            Assert.IsType<NumberToken>(tokens[0]);
            Assert.Equal(1, tokens[0].Lexeme.Length);
            Assert.IsType<OperatorPlusToken>(tokens[1]);
            Assert.IsType<NumberToken>(tokens[2]);
            Assert.Equal(1, tokens[2].Lexeme.Length);
        }

        [Fact]
        public void TestNumber55Plus5()
        {
            const string input = "55+5";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(3, tokens.Count);
            Assert.IsType<NumberToken>(tokens[0]);
            Assert.Equal(2, tokens[0].Lexeme.Length);
            Assert.IsType<OperatorPlusToken>(tokens[1]);
            Assert.IsType<NumberToken>(tokens[2]);
            Assert.Equal(1, tokens[2].Lexeme.Length);
        }

        [Fact]
        public void TestAllDigitsPlusAllDigits()
        {
            const string input = "0123456789+0123456789";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(3, tokens.Count);
            Assert.IsType<NumberToken>(tokens[0]);
            Assert.Equal(10, tokens[0].Lexeme.Length);
            Assert.IsType<OperatorPlusToken>(tokens[1]);
            Assert.IsType<NumberToken>(tokens[2]);
            Assert.Equal(10, tokens[2].Lexeme.Length);
        }

        [Fact]
        public void TestOperatorMinusNegativeNumber()
        {
            const string input = "--10";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(3, tokens.Count);
            Assert.IsType<OperatorMinusToken>(tokens[0]);
            Assert.IsType<OperatorMinusToken>(tokens[1]);
            Assert.IsType<NumberToken>(tokens[2]);
            Assert.Equal(2, tokens[2].Lexeme.Length);
        }

        [Fact]
        public void TestNumberMinusNegativeNumber()
        {
            const string input = "10--10";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(4, tokens.Count);
            Assert.IsType<NumberToken>(tokens[0]);
            Assert.IsType<OperatorMinusToken>(tokens[1]);
            Assert.IsType<OperatorMinusToken>(tokens[2]);
            Assert.IsType<NumberToken>(tokens[3]);
            Assert.Equal(2, tokens[3].Lexeme.Length);
        }

        [Fact]
        public void TestNegativeNumberMinusNegativeNumber()
        {
            const string input = "-50--999";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(5, tokens.Count);

            Assert.IsType<OperatorMinusToken>(tokens[0]);

            Assert.IsType<NumberToken>(tokens[1]);
            Assert.Equal(2, tokens[1].Lexeme.Length);

            Assert.IsType<OperatorMinusToken>(tokens[2]);
            Assert.IsType<OperatorMinusToken>(tokens[3]);

            Assert.IsType<NumberToken>(tokens[4]);
            Assert.Equal(3, tokens[4].Lexeme.Length);
        }

        [Fact]
        public void TestNegativeNumberPlusNegativeNumber()
        {
            const string input = "-50+-999";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(5, tokens.Count);

            Assert.IsType<OperatorMinusToken>(tokens[0]);

            Assert.IsType<NumberToken>(tokens[1]);
            Assert.Equal(2, tokens[1].Lexeme.Length);

            Assert.IsType<OperatorPlusToken>(tokens[2]);

            Assert.IsType<OperatorMinusToken>(tokens[3]);
            Assert.IsType<NumberToken>(tokens[4]);
            Assert.Equal(3, tokens[4].Lexeme.Length);
        }

        [Fact]
        public void TestNegativeNumberTimesNegativeNumber()
        {
            const string input = "-50*-999";

            var tokenizer = new Tokenizer();
            bool result = tokenizer.Tokenize(input, out var tokens);

            Assert.True(result);
            Assert.Equal(5, tokens.Count);

            Assert.IsType<OperatorMinusToken>(tokens[0]);

            Assert.IsType<NumberToken>(tokens[1]);
            Assert.Equal(2, tokens[1].Lexeme.Length);

            Assert.IsType<OperatorMultiplicationToken>(tokens[2]);

            Assert.IsType<OperatorMinusToken>(tokens[3]);

            Assert.IsType<NumberToken>(tokens[4]);
            Assert.Equal(3, tokens[4].Lexeme.Length);
        }
    }
}