using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;


#pragma warning disable IDE0005
using Serilog = Meryel.Serilog;
#pragma warning restore IDE0005


#nullable enable


namespace Meryel.UnityCodeAssist.Editor.Input
{

    public class Text2Yaml
    {
        public static string Convert(IEnumerable<string> textLines)
        {
            var sb = new StringBuilder();
            var stack = new Stack<(string typeName, string identifier, int indentation)>();

            sb.AppendLine(@"%YAML 1.1");
            sb.AppendLine(@"%TAG !u! tag:unity3d.com,2011:");
            sb.AppendLine(@"--- !u!13 &1");
            sb.AppendLine(@"InputManager:");

            var regexIndentation = new Regex("^\\s*");

            var regexString = new Regex("^(\\s+)(\\w+)\\s+\"([a-zA-Z0-9_ \\/\\.\\-]*)\"\\s+\\(string\\)$");
            var regexValue = new Regex("^(\\s+)(\\w+)\\s+(-?[0-9.]*)\\s+\\(((bool)|(int)|(float)|(unsigned int))\\)$");
            var regexType = new Regex("^(\\s+)(\\w+)\\s+\\((\\w+)\\)$");

            var regexVectorSize = new Regex("(\\s+)size\\s+(\\d)+\\s+\\(int\\)");
            //var regexVectorData = new Regex("(\\s+)data  \\(InputAxis\\)"); // remove InputAxis to make it more generic

            string curTextLine;
            var curTextLineNo = 3;
            var textIndentation = 1;
            var indentationPrefix = new string(' ', textIndentation * 2);
            stack.Push(("InputManager", "InputManager", textIndentation));


            foreach (var line in textLines.Skip(4))
            {
                curTextLine = line;
                curTextLineNo++;


                // Skip empty lines
                if (line.Length == 0)
                    continue;

                // Check if type undeclared, scope goes down, indentation decrements
                {
                    var indentationMatch = regexIndentation.Match(line);
                    if (indentationMatch.Success)
                    {
                        var indentation = indentationMatch.Groups[0].Value.Length;

                        if (indentation > textIndentation)
                            Error($"indentation({indentation}) > textIndentation({textIndentation})");

                        while (indentation < textIndentation)
                        {
                            stack.Pop();
                            textIndentation--;
                            var typeIndentation = textIndentation;
                            if (stack.TryPeek(out var curType2))
                                typeIndentation = curType2.indentation;
                            else if (line.Length > 0)
                                Error("stack empty at type undeclaration");
                            indentationPrefix = new string(' ', typeIndentation * 2);
                        }

                    }
                    else
                    {
                        Error($"{nameof(regexIndentation)} failed");
                    }
                }

                // Skip size field of vectors
                if (stack.TryPeek(out var curType1) && curType1.typeName == "vector")
                {
                    var vectorSizeMatch = regexVectorSize.Match(line);
                    if (vectorSizeMatch.Success)
                    {
                        continue;
                    }
                }

                // Read string fields
                {
                    var stringMatch = regexString.Match(line);
                    if (stringMatch.Success)
                    {
                        AddLine(stringMatch.Groups[2] + ": " + stringMatch.Groups[3]);
                        continue;
                    }
                }

                // Read bool/int/float/unsignedInt fields
                {
                    var valueMatch = regexValue.Match(line);
                    if (valueMatch.Success)
                    {
                        AddLine(valueMatch.Groups[2] + ": " + valueMatch.Groups[3]);
                        continue;
                    }
                }

                // Check if new type declared, scope goes up, indentation increases
                {
                    var typeMatch = regexType.Match(line);
                    if (typeMatch.Success)
                    {
                        var identifier = typeMatch.Groups[2].Value;
                        var typeName = typeMatch.Groups[3].Value;

                        var isVectorData = false;
                        if (stack.TryPeek(out var curType2) && curType2.typeName == "vector" && identifier == "data")
                            isVectorData = true;

                        var typeIndentation = textIndentation;
                        if (stack.TryPeek(out var curType3))
                            typeIndentation = curType3.indentation;
                        else if (line.Length > 0)
                            Error("stack empty at type declaration");

                        if (!isVectorData)
                        {
                            AddLine(typeMatch.Groups[2] + ":");
                        }
                        else
                        {
                            var customIndentation = typeIndentation - 1;
                            if (customIndentation < 0)
                                Error($"customIndentation({customIndentation}) < 0");
                            var customIndentationPrefix = new string(' ', customIndentation * 2);
                            AddLine("- serializedVersion: 3", customIndentationPrefix);
                        }


                        textIndentation++;
                        typeIndentation++;

                        if (isVectorData)
                            typeIndentation--;

                        stack.Push((typeName, identifier, typeIndentation));
                        indentationPrefix = new string(' ', typeIndentation * 2);

                        continue;
                    }
                }


                Error("line failed to match all cases");

            }



            return sb.ToString();


            void AddLine(string line, string? customIndentationPrefix = null)
            {
                string suffix;
                if (stack.TryPeek(out var top))
                    suffix = $" # {textIndentation}, {top.indentation}, {top.typeName} {top.identifier}";
                else
                    suffix = $" # {textIndentation}, nil";

                if (customIndentationPrefix != null)
                    sb.AppendLine(customIndentationPrefix + line + suffix);
                else
                    sb.AppendLine(indentationPrefix + line + suffix);
            }

            void Error(string message)
            {
                var errorMessage = $"Text2Yaml error '{message}' at lineNo: {curTextLineNo}, line: '{curTextLine}' at {Environment.StackTrace}";
                //throw new Exception(errorMessage);
                Serilog.Log.Warning(errorMessage);
            }

        }


    }

    public static partial class Extensions
    {
        public static bool TryPeek<T>(this Stack<T> stack, /*[MaybeNullWhen(false)]*/ out T result)
        {
            if (stack.Count > 0)
            {
                result = stack.Peek();
                return true;
            }
            else
            {
                result = default!;
                return false;
            }
        }
    }

}