using System.Text.RegularExpressions;

namespace VisionBuilder.UI.Recipes.Scripted;

public enum ETokenType
{
    EOF,
    UnexpectedToken,
    Identifier,
    String,


    OpenParenthesis,
    CloseParenthesis
}
public class Token
{
    public Token(ETokenType type, string value, int line, int column, int start, int end)
    {
        Type = type;
        Value = value;
        Line = line;
        Column = column;
        Start = start;
        End = end;
    }



    public ETokenType Type { get; set; }
    public string Value { get; set; }
    public int Line { get; set; }
    public int Column { get; set; }
    public int Start { get; set; }
    public int End { get; set; }

    public override string ToString()
    {
        return string.Format("{0} ({1})", Value, Type);
    }


}
public class Tokenizer
{

    public int Position { get; private set; }
    public string Code { get; private set; }

    public Tokenizer(string code)
    {
        Code = code;
    }

    private Dictionary<string, ETokenType?> _specs = new()
    {
        // comment
        {@"^\/\/[^\n]*",null},
        // multi-line comment
        {@"^\/\*.*\*\/",null},
        // whitespace
        {@"^\s+",null},

        {"^\"(\\\"|[^\"])*'",ETokenType.String},

        {@"^\(",ETokenType.OpenParenthesis},
        {@"^\)",ETokenType.CloseParenthesis},
        {@"^[a-zA-Z_][a-zA-Z0-9_]*",ETokenType.Identifier},
    };


    private int GetCurrentLine()
    {
        var line = 1;
        for (var i = 0; i < Position; i++)
        {
            if (Code[i] == '\n')
                line++;
        }

        return line;

    }

    private int GetCurrentColumn()
    {
        var column = 1;
        for (var i = 0; i < Position; i++)
        {
            if (Code[i] == '\n')
                column = 1;
            else
                column++;
        }

        return column;
    }

    public bool HasNextToken()
    {
        return Position < Code.Length;
    }
    public Token NextToken()
    {
        if (!HasNextToken())
            return new Token(ETokenType.EOF, string.Empty, GetCurrentLine(), GetCurrentColumn(), Position, Position);
        var line = GetCurrentLine();
        var column = GetCurrentColumn();
        if (column == 1)
        {

            if (Regex.IsMatch(Code.Substring(Position), @"^\s*\*"))
            {
                var match = Regex.Match(Code.Substring(Position), @"^\s*\*[^\n]*");
                Position += match.Length;
                return NextToken();
            }
        }
        foreach (var spec in _specs)
        {
            var match = Regex.Match(Code.Substring(Position), spec.Key);
            if (match.Success)
            {

                var start = Position;
                Position += match.Length;
                if (spec.Value.HasValue)
                {
                    if (spec.Value.Value == ETokenType.String)
                    {
                        return new Token(spec.Value.Value, Regex.Unescape(match.Value.Substring(1, match.Value.Length - 2)), line, column, start, Position);
                    }
                    return new Token(spec.Value.Value, match.Value, line, column, start, Position);
                }

                else
                    return NextToken();
            }
        }

        return new Token(ETokenType.UnexpectedToken, Code.Substring(Position, 1), line, column, Position, Position + 1);
    }
}