using ZelWiki.Engine.Function.Exceptions;

namespace ZelWiki.Engine.Function
{
    public class FunctionPrototypeCollection
    {
        public enum WikiFunctionType
        {
            Standard,
            Scoped,
            Instruction
        }

        public WikiFunctionType FunctionTypes { get; private set; }
        public List<PrototypeSet> Items { get; set; } = new();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="functionTypes"></param>
        public FunctionPrototypeCollection(WikiFunctionType functionTypes)
        {
            FunctionTypes = functionTypes;
        }

        public void Add(string prototypeString)
        {
            var prototype = ParsePrototype(prototypeString);

            Items.Add(new PrototypeSet()
            {
                FunctionPrefix = prototype.FunctionPrefix,
                ProperName = prototype.ProperName,
                FunctionName = prototype.FunctionName.ToLower(),
                Value = prototype
            });
        }

        public bool Exists(string functionPrefix, string functionName)
        {
            functionName = functionName.ToLower();

            return Items.Any(o =>
                (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName);
        }

        public FunctionPrototype Get(string functionPrefix, string functionName)
        {
            functionName = functionName.ToLower();

            var functionPrototype = Items.FirstOrDefault(o =>
                    (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName)
                ?.Value;

            return functionPrototype
                   ?? throw new WikiFunctionPrototypeNotDefinedException(
                       $"函数 ({functionName}) 没有定义的原型.");
        }
        
        #region Private

        private FunctionPrototype ParsePrototype(string prototypeString)
        {
            var nameStartIndex = prototypeString.TakeWhile(c => char.IsLetterOrDigit(c) == false).Count();
            var nameEndIndex = prototypeString.IndexOf(':');
            var properName = prototypeString.Substring(nameStartIndex, nameEndIndex - nameStartIndex).Trim();
            var functionName = properName.ToLower();
            var functionPrefix = prototypeString.Substring(0, nameStartIndex).Trim();

            prototypeString = prototypeString.Substring(nameEndIndex + 1).Trim();

            var prototype = new FunctionPrototype()
                { FunctionPrefix = functionPrefix, ProperName = properName, FunctionName = functionName };

            if (prototypeString.Length == 0)
                return prototype;

            var segments = prototypeString.Trim().Split('|').Select(o => o.Trim());

            foreach (var segment in segments)
            {
                var prototypeSegment = new PrototypeParameter();

                var index = 0;

                if (segment[index] == '<')
                {
                    index++;
                    prototypeSegment.Type = Tok(segment, ref index);
                    index++;

                    if (prototypeSegment.Type.Contains(':'))
                    {
                        var splitSeg = prototypeSegment.Type.Split(':');
                        prototypeSegment.Type = splitSeg[0];
                        if (splitSeg[1].Equals("infinite", StringComparison.InvariantCultureIgnoreCase))
                        {
                            prototypeSegment.IsInfinite = true;
                            if (prototype.Parameters.Any(o => o.IsInfinite))
                            {
                                throw new Exception(
                                    $"函数 [{functionName}], 原型错误: cannot contain more than one [infinite] parameter.");
                            }
                        }
                        else
                        {
                            throw new Exception(
                                $"函数 [{functionName}], 原型错误: expected [infinite] got [{splitSeg[1]}].");
                        }
                    }

                    SkipWhiteSpace(segment, ref index);

                    if (index < segment.Length && segment[index] == '{' || segment[index] == '[')
                    {
                        if (index < segment.Length && segment[index] == '[')
                        {
                            prototypeSegment.IsRequired = true;
                        }

                        index++;

                        prototypeSegment.Name = Tok(segment, ref index);

                        if (index < segment.Length && segment[index] == '(')
                        {
                            int allowedValueEndIndex = segment.IndexOf(')', index);
                            string roteRequiredValues = segment.Substring(index + 1, allowedValueEndIndex - index - 1);
                            prototypeSegment.AllowedValues = roteRequiredValues.Trim().Split(',')
                                .Select(o => o.Trim().ToLower()).ToList();

                            index = allowedValueEndIndex;
                            index++;
                            SkipWhiteSpace(segment, ref index);
                        }

                        index++;
                    }
                    else
                    {
                        throw new Exception($"函数 [{functionName}], 原型错误: expected [{{] or [[].");
                    }

                    SkipWhiteSpace(segment, ref index);

                    if (index < segment.Length && segment[index] == '=')
                    {
                        index++;
                        SkipWhiteSpace(segment, ref index);

                        if (segment[index] != '\'')
                        {
                            throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
                        }

                        index++;

                        prototypeSegment.DefaultValue = segment.Substring(index, (segment.Length - index) - 1);

                        index = segment.Length - 1;

                        if (index < segment.Length && segment[index] != '\'')
                        {
                            throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
                        }
                    }
                }
                else
                {
                    throw new Exception($"函数 [{functionName}], 原型错误: expected [<].");
                }

                prototype.Parameters.Add(prototypeSegment);
            }

            return prototype;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="str"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private string Tok(string str, ref int index)
        {
            var token = string.Empty;

            SkipWhiteSpace(str, ref index);

            for (; index < str.Length; index++)
            {
                if ("<>{}[]()".Contains(str[index]))
                {
                    break;
                }

                token += str[index];
            }

            SkipWhiteSpace(str, ref index);

            return token;
        }

        private static void SkipWhiteSpace(string str, ref int index)
        {
            while (index < str.Length && char.IsWhiteSpace(str[index]))
            {
                index++;
            }
        }
        
        #endregion
    }
}