namespace CanonSharp.Common.LexicalAnalyzer;
public abstract class RegularExpression
{
    public abstract NondeterministicFiniteAutomation Convert2Nfa();
    /// 
    /// 匹配空字符串
    /// 
    public static RegularExpression Empty => new EmptyExpression();
    /// 
    /// 匹配单个字符
    /// c
    /// 
    /// 
    /// 
    public static RegularExpression Single(char c) => new SymbolExpression(c);
    /// 
    /// left|right
    /// 
    /// 
    /// 
    /// 
    public static RegularExpression Alternate(RegularExpression left, RegularExpression right) =>
        new AlternationExpression(left, right);
    /// 
    /// left-right
    /// 
    /// 
    /// 
    /// 
    public static RegularExpression Concatenate(RegularExpression first, RegularExpression second) =>
        new ConcatenationExpression(first, second);
    /// 
    /// inner*
    /// 
    /// 
    /// 
    public static RegularExpression Kleene(RegularExpression inner) => new KleeneExpression(inner);
    /// 
    /// value
    /// 
    /// 
    /// 
    public static RegularExpression String(string value) => new StringExpression(value);
    public static RegularExpression CharSetOf(string value) => new CharSetExpression(value.ToCharArray());
    public static RegularExpression CharSetOf(Func predicate)
        => new CharSetExpression(Iterate(char.MinValue, char.MaxValue).Where(predicate).ToArray());
    /// 
    /// [a-b]
    /// 
    /// 
    /// 
    /// 
    public static RegularExpression Range(char a, char b) => new CharSetExpression(Iterate(a, b).ToArray());
    private static IEnumerable Iterate(char a, char b)
    {
        for (char c = a; c <= b; c++)
        {
            if (c == char.MaxValue)
            {
                yield break;
            }
            yield return c;
        }
    }
}
public class EmptyExpression : RegularExpression
{
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicState final = new();
        NondeterministicState start = new();
        start.AddTransaction(EmptyChar.Empty, final);
        return new NondeterministicFiniteAutomation(start, [final]);
    }
}
public class SymbolExpression(char symbol) : RegularExpression
{
    public char Symbol { get; } = symbol;
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicState final = new();
        NondeterministicState start = new();
        start.AddTransaction(new EmptyChar(Symbol), final);
        return new NondeterministicFiniteAutomation(start, [final]);
    }
}
public class AlternationExpression(RegularExpression left, RegularExpression right) : RegularExpression
{
    public RegularExpression Left { get; } = left;
    public RegularExpression Right { get; } = right;
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicFiniteAutomation left = Left.Convert2Nfa();
        NondeterministicFiniteAutomation right = Right.Convert2Nfa();
        NondeterministicState final = new();
        foreach (NondeterministicState state in left.FinalStates.Concat(right.FinalStates))
        {
            state.AddTransaction(EmptyChar.Empty, final);
        }
        NondeterministicState start = new();
        start.AddTransaction(EmptyChar.Empty, left.Start);
        start.AddTransaction(EmptyChar.Empty, right.Start);
        return new NondeterministicFiniteAutomation(start, [final]);
    }
}
public class ConcatenationExpression(RegularExpression first, RegularExpression second) : RegularExpression
{
    public RegularExpression First { get; } = first;
    public RegularExpression Second { get; } = second;
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicFiniteAutomation first = First.Convert2Nfa();
        NondeterministicFiniteAutomation second = Second.Convert2Nfa();
        foreach (NondeterministicState state in first.FinalStates)
        {
            state.AddTransaction(EmptyChar.Empty, second.Start);
        }
        return new NondeterministicFiniteAutomation(first.Start, second.FinalStates);
    }
}
public class KleeneExpression(RegularExpression inner) : RegularExpression
{
    public RegularExpression Inner { get; } = inner;
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicFiniteAutomation inner = Inner.Convert2Nfa();
        NondeterministicState final = new();
        final.AddTransaction(EmptyChar.Empty, inner.Start);
        foreach (NondeterministicState state in inner.FinalStates)
        {
            state.AddTransaction(EmptyChar.Empty, final);
        }
        return new NondeterministicFiniteAutomation(final, [final]);
    }
}
public class CharSetExpression : RegularExpression
{
    public char[] Set { get; }
    public CharSetExpression(Span set)
    {
        if (set.Length == 0)
        {
            throw new InvalidOperationException();
        }
        Set = set.ToArray();
    }
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicState start = new();
        NondeterministicState final = new();
        foreach (char c in Set)
        {
            start.AddTransaction(new EmptyChar(c), final);
        }
        return new NondeterministicFiniteAutomation(start, [final]);
    }
}
public class StringExpression : RegularExpression
{
    public string Word { get; }
    public StringExpression(string word)
    {
        if (string.IsNullOrEmpty(word))
        {
            throw new InvalidOperationException();
        }
        Word = word;
    }
    public override NondeterministicFiniteAutomation Convert2Nfa()
    {
        NondeterministicState start = new();
        NondeterministicState final = Word.Aggregate(start, (state, c) =>
        {
            NondeterministicState next = new();
            state.AddTransaction(new EmptyChar(c), next);
            return next;
        });
        return new NondeterministicFiniteAutomation(start, [final]);
    }
}