using FastJSON;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Xml.Serialization;

namespace Top.Api.Parser
{
    /// <summary>
    /// TOP JSON响应通用解释器。
    /// </summary>
    public class DingTalkJsonParser<T> : ITopParser<T> where T : TopResponse
    {
        private static readonly ReaderWriterLock rwLock = new ReaderWriterLock();
        private static readonly Dictionary<string, Dictionary<string, TopAttribute>> attrs = new Dictionary<string, Dictionary<string, TopAttribute>>();

        #region ITopParser Members

        public virtual T Parse(string body)
        {
            return Parse(body, typeof(T));
        }

        public T Parse(string body, Type type)
        {
            T rsp = null;

            IDictionary json = JSON.Parse(body) as IDictionary;
            if (json != null)
            {
                IDictionary data = json;

                if (data != null)
                {
                    ITopReader reader = new TopSimplifyJsonReader(data);
                    rsp = (T)FromJson(reader, type);
                }
            }

            if (rsp == null)
            {
                rsp = Activator.CreateInstance(type) as T;
            }

            if (rsp != null)
            {
                rsp.Body = body;
            }

            return rsp;
        }

        #endregion

        private Dictionary<string, TopAttribute> GetTopAttributes(Type type)
        {
            Dictionary<string, TopAttribute> tas = null;
            bool inc = false;

            rwLock.AcquireReaderLock(50);
            try
            {
                if (rwLock.IsReaderLockHeld)
                {
                    inc = attrs.TryGetValue(type.FullName, out tas);
                }
            }
            finally
            {
                if (rwLock.IsReaderLockHeld)
                {
                    rwLock.ReleaseReaderLock();
                }
            }

            if (inc && tas != null) // 从缓存中获取类属性元数据
            {
                return tas;
            }
            else // 创建新的类属性元数据缓存
            {
                tas = new Dictionary<string, TopAttribute>();
            }

            PropertyInfo[] pis = type.GetProperties();
            foreach (PropertyInfo pi in pis)
            {
                TopAttribute ta = new TopAttribute();
                ta.Method = pi.GetSetMethod();

                // 获取对象属性名称
                XmlElementAttribute[] xeas = pi.GetCustomAttributes(typeof(XmlElementAttribute), true) as XmlElementAttribute[];
                if (xeas != null && xeas.Length > 0)
                {
                    ta.ItemName = xeas[0].ElementName;
                }

                // 获取列表属性名称
                if (ta.ItemName == null)
                {
                    XmlArrayItemAttribute[] xaias = pi.GetCustomAttributes(typeof(XmlArrayItemAttribute), true) as XmlArrayItemAttribute[];
                    if (xaias != null && xaias.Length > 0)
                    {
                        ta.ItemName = xaias[0].ElementName;
                    }
                    XmlArrayAttribute[] xaas = pi.GetCustomAttributes(typeof(XmlArrayAttribute), true) as XmlArrayAttribute[];
                    if (xaas != null && xaas.Length > 0)
                    {
                        ta.ListName = xaas[0].ElementName;
                    }
                    if (ta.ListName == null)
                    {
                        continue;
                    }
                }

                // 获取属性类型
                if (pi.PropertyType.IsGenericType)
                {
                    Type[] types = pi.PropertyType.GetGenericArguments();
                    ta.ListType = types[0];
                }
                else
                {
                    ta.ItemType = pi.PropertyType;
                }

                tas.Add(pi.Name, ta);
            }

            rwLock.AcquireWriterLock(50);
            try
            {
                if (rwLock.IsWriterLockHeld)
                {
                    attrs[type.FullName] = tas;
                }
            }
            finally
            {
                if (rwLock.IsWriterLockHeld)
                {
                    rwLock.ReleaseWriterLock();
                }
            }
            return tas;
        }

        public object FromJson(ITopReader reader, Type type)
        {
            object rsp = null;
            Dictionary<string, TopAttribute> pas = GetTopAttributes(type);

            Dictionary<string, TopAttribute>.Enumerator em = pas.GetEnumerator();
            while (em.MoveNext())
            {
                KeyValuePair<string, TopAttribute> kvp = em.Current;
                TopAttribute ta = kvp.Value;
                string itemName = ta.ItemName;
                string listName = ta.ListName;

                if (!reader.HasReturnField(itemName) && (string.IsNullOrEmpty(listName) || !reader.HasReturnField(listName)))
                {
                    continue;
                }

                object value = null;
                if (ta.ListType != null)
                {
                    value = reader.GetListObjects(listName, itemName, ta.ListType, FromJson);
                }
                else
                {
                    if (typeof(string) == ta.ItemType)
                    {
                        object tmp = reader.GetPrimitiveObject(itemName);
                        if (typeof(string) == tmp.GetType())
                        {
                            value = tmp;
                        }
                        else
                        {
                            if (tmp != null)
                            {
                                value = JSON.ToJSON(tmp);
                            }
                        }
                    }
                    else if (typeof(long) == ta.ItemType)
                    {
                        object tmp = reader.GetPrimitiveObject(itemName);
                        if (typeof(long) == tmp.GetType())
                        {
                            value = tmp;
                        }
                        else
                        {
                            if (tmp != null)
                            {
                                value = long.Parse(tmp.ToString());
                            }
                        }
                    }
                    else if (typeof(bool) == ta.ItemType)
                    {
                        object tmp = reader.GetPrimitiveObject(itemName);
                        if (typeof(bool) == tmp.GetType())
                        {
                            value = tmp;
                        }
                        else
                        {
                            if (tmp != null)
                            {
                                value = bool.Parse(tmp.ToString());
                            }
                        }
                    }
                    else
                    {
                        value = reader.GetReferenceObject(itemName, ta.ItemType, FromJson);
                    }
                }

                if (value != null)
                {
                    if (rsp == null)
                    {
                        rsp = Activator.CreateInstance(type);
                    }
                    ta.Method.Invoke(rsp, new object[] { value });
                }
            }

            return rsp;
        }
    }
}
