using XGame.Framework.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace XGame.Framework.Json
{
    public class JsonParsor
    {
        private const int TokenNone = 0;
        /// <summary>
        /// 花括号 {
        /// </summary>
        private const int TokenCurlyOpen = 1;
        /// <summary>
        /// 花括号 }
        /// </summary>
        private const int TokenCurlyClose = 2;
        /// <summary>
        /// 
        /// </summary>
        private const int TokenSquaredOpen = 3;
        /// <summary>
        /// 方括号 ]
        /// </summary>
        private const int TokenSquaredClose = 4;
        /// <summary>
        /// 冒号 :
        /// </summary>
        private const int TokenColon = 5;
        /// <summary>
        /// 逗号 ,
        /// </summary>
        private const int TokenComma = 6;
        /// <summary>
        /// 字符串 string
        /// </summary>
        private const int TokenString = 7;
        /// <summary>
        /// 数字 number
        /// </summary>
        private const int TokenNumber = 8;
        /// <summary>
        /// bool值 true
        /// </summary>
        private const int TokenTrue = 9;
        /// <summary>
        /// bool值 false
        /// </summary>
        private const int TokenFalse = 10;
        /// <summary>
        /// 空 null
        /// </summary>
        private const int TokenNull = 11;

        public bool TryDeserializeObject(string json, out object obj)
        {
            bool success = true;
            obj = null;

            if (json != null)
            {
                char[] charArray = json.ToCharArray();
                int index = 0;
                obj = ParseValue(charArray, ref index, ref success);
                //PrintDict(obj);
            }

            return success;
        }

        #region 对json进行解析

        // 开始处理json字符串的每个字符
        private object ParseValue(char[] json, ref int index, ref bool success)
        {
            switch (LookAhead(json, index))
            {
                case TokenString:
                    return ParseString(json, ref index, ref success);
                case TokenNumber:
                    return ParseNumber(json, ref index, ref success);
                case TokenCurlyOpen:
                    return ParseObject(json, ref index, ref success);
                case TokenSquaredOpen:
                    return ParseArray(json, ref index, ref success);
                case TokenTrue:
                    NextToken(json, ref index);
                    return true;
                case TokenFalse:
                    NextToken(json, ref index);
                    return false;
                case TokenNull:
                    NextToken(json, ref index);
                    return null;
                case TokenNone:
                    break;
            }
            success = false;
            return null;
        }

        // 特殊字符跳过
        private void DealWithWhitespace(char[] json, ref int index)
        {
            for (int length = json.Length; index < length; index++)
            {
                if (" \t\n\r\b\f".IndexOf(json[index]) == -1)
                    break;
            }
        }

        // 前瞻
        private int LookAhead(char[] json, int index)
        {
            int saveIndex = index;
            return NextToken(json, ref saveIndex);
        }

        // json字符判断
        private int NextToken(char[] json, ref int index)
        {
            DealWithWhitespace(json, ref index);
            if (index == json.Length) return TokenNone;

            char c = json[index];
            index++;

            // 字符判断
            switch (c)
            {
                case '{':
                    return TokenCurlyOpen;
                case '}':
                    return TokenCurlyClose;
                case '[':
                    return TokenSquaredOpen;
                case ']':
                    return TokenSquaredClose;
                case ':':
                    return TokenColon;
                case ',':
                    return TokenComma;
                case '"':
                    return TokenString;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '-':
                    return TokenNumber;
            }
            // 剩余字符长度
            index--;
            int remainingLength = json.Length - index;

            // 字符串false
            if (remainingLength >= 5)
            {
                if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e')
                {
                    index += 5;
                    return TokenFalse;
                }
            }

            // 字符串true
            if (remainingLength >= 4)
            {
                if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e')
                {
                    index += 4;
                    return TokenTrue;
                }
            }

            // 字符串null
            if (remainingLength >= 4)
            {
                if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l')
                {
                    index += 4;
                    return TokenNull;
                }
            }

            return TokenNone;
        }

        #region ParseString

        private string ParseString(char[] json, ref int index, ref bool success)
        {
            StringBuilder builder = StringBuilderUtils.Acquire();

            DealWithWhitespace(json, ref index);

            char c = json[index++];
            bool complete = false;
            while (!complete)
            {
                if (index == json.Length) break;

                c = json[index++];
                switch (c)
                {
                    case '"':
                        complete = true;
                        break;
                    case '\\':
                        if (index == json.Length) break;

                        c = json[index++];
                        switch (c)
                        {
                            case '"':
                                builder.Append('"');
                                break;
                            case '\\':
                                builder.Append('\\');
                                break;
                            case '\'':
                                builder.Append('\'');
                                break;
                            case '/':
                                builder.Append('/');
                                break;
                            case 'b':
                                builder.Append('\b');
                                break;
                            case 'f':
                                builder.Append('\f');
                                break;
                            case 'n':
                                builder.Append('\n');
                                break;
                            case 'r':
                                builder.Append('\r');
                                break;
                            case 't':
                                builder.Append('\t');
                                break;
                            case 'u':
                                int remainingLength = json.Length - index;
                                if (remainingLength >= 4)
                                {
                                    uint codePoint;
                                    // 将32位十六进制解析为无符号整型
                                    if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint)))
                                        throw new SystemException("can not parse the 32 bit hex into an integer codepoint");

                                    // 将整型转换为unicode字符并添加到字符串中
                                    if (0xD800 <= codePoint && codePoint <= 0xDBFF)  // if high surrogate
                                    {
                                        index += 4; // skip 4 chars
                                        remainingLength = json.Length - index;
                                        if (remainingLength >= 6)
                                        {
                                            uint lowCodePoint;
                                            if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint))
                                            {
                                                if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF)    // if low surrogate
                                                {
                                                    builder.Append((char)codePoint);
                                                    builder.Append((char)lowCodePoint);
                                                    index += 6; // skip 6 chars
                                                    continue;
                                                }
                                            }
                                        }
                                        success = false;    // invalid surrogate pair
                                        StringBuilderUtils.Release(builder);
                                        return "";
                                    }
                                    builder.Append(ConvertFromUtf32((int)codePoint));
                                    // skip 4 chars
                                    index += 4;
                                }
                                break;
                        }
                        break;
                    default:
                        builder.Append(c);
                        break;
                }
            }

            if (!complete)
            {
                success = false;
                StringBuilderUtils.Release(builder);
                return null;
            }

            return StringBuilderUtils.GetStringAndRelease(builder);
        }

        // Unicode编码转换为UTF-32
        private string ConvertFromUtf32(int utf32)
        {
            if (utf32 < 0 || utf32 > 0x10FFFF)
                throw new ArgumentOutOfRangeException(nameof(utf32), "The argument must be from 0 to 0x10FFFF.");
            if (0xD800 <= utf32 && utf32 <= 0xDFFF)
                throw new ArgumentOutOfRangeException(nameof(utf32), "The argument must not be in surrogate pair range.");
            if (utf32 < 0x10000)
                return new string((char)utf32, 1);
            utf32 -= 0x10000;
            return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) });
        }

        #endregion

        #region ParseNumber

        private object ParseNumber(char[] json, ref int index, ref bool success)
        {
            DealWithWhitespace(json, ref index);
            // 获取数字的字符串
            int lastIndex = GetLastIndexOfNumber(json, index);
            int numLength = (lastIndex - index) + 1;
            string numStr = new string(json, index, numLength);
            object changeNum;
            if (numStr.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || numStr.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1)
            {
                success = double.TryParse(new string(json, index, numLength), NumberStyles.Any, CultureInfo.InvariantCulture, out var number);
                changeNum = number;
            }
            else
            {
                success = long.TryParse(new string(json, index, numLength), NumberStyles.Any, CultureInfo.InvariantCulture, out var number);
                changeNum = number;
            }

            index = lastIndex + 1;
            return changeNum;
        }

        private int GetLastIndexOfNumber(char[] json, int index)
        {
            int lastIndex = index;
            for (int length = json.Length; lastIndex < length; lastIndex++)
            {
                if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break;
            }
            return lastIndex - 1;
        }

        #endregion

        #region ParseObject

        private Dictionary<string, object> ParseObject(char[] json, ref int index, ref bool success)
        {
            Dictionary<string, object> map = new Dictionary<string, object>();

            DealWithWhitespace(json, ref index);
            // 跳过 {
            index++;
            int token = index;

            bool complete = false;
            while (!complete)
            {
                token = LookAhead(json, index);
                switch (token)
                {
                    case TokenNone:
                        success = false;
                        return null;
                    case TokenComma:
                        NextToken(json, ref index);
                        break;
                    case TokenCurlyClose:
                        NextToken(json, ref index);
                        return map;
                    default:
                        // key
                        string key = ParseString(json, ref index, ref success);
                        if (!success) return null;
                        // :
                        token = NextToken(json, ref index);
                        if (token != TokenColon)
                        {
                            success = false;
                            return null;
                        }
                        // value
                        object value = ParseValue(json, ref index, ref success);
                        if (!success) return null;

                        map.Add(key, value);
                        break;
                }
            }

            return map;
        }


        #endregion

        #region ParseArray

        private List<object> ParseArray(char[] json, ref int index, ref bool success)
        {
            List<object> list = new List<object>();

            DealWithWhitespace(json, ref index);
            index++;
            int token = index;

            bool complete = false;
            while (!complete)
            {
                token = LookAhead(json, index);
                switch (token)
                {
                    case TokenNone:
                        success = false;
                        return null;
                    case TokenComma:
                        NextToken(json, ref index);
                        break;
                    case TokenSquaredClose:
                        NextToken(json, ref index);
                        complete = true;
                        break;
                    default:
                        // value
                        object value = ParseValue(json, ref index, ref success);
                        if (!success) return null;
                        list.Add(value);
                        break;
                }
            }
            return list;
        }

        #endregion

        #endregion
    }
}
