using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace SMCryptoUtils
{
    [ClassInterface(ClassInterfaceType.None)]
    public class SignUtil:ISignUtil
    {
        private static List<string> ignoreSign = new List<string>() { "signData", "encData", "extra" };

        public string getSignText(JObject jsonObject, string appSecret)
        {
            SortedDictionary<string, string> signMap = new SortedDictionary<string, string>(StringComparer.Ordinal);

            foreach (var entry in jsonObject)
            {
                if (!string.IsNullOrEmpty(entry.Value.ToString()) && !ignoreSign.Contains(entry.Key))
                {
                    signMap.Add(entry.Key, getValue(entry.Value));
                }
            }


            List<string> list = new List<string>();

            foreach (var entry in signMap)
            {
                if (!string.IsNullOrEmpty(getObjString(entry.Value)))
                {
                    list.Add((string)entry.Key + "=" + (string)entry.Value + "&");
                }
            }

            int size = list.Count();
            string[] arrayToSort = (string[])list.ToArray();
            Array.Sort(arrayToSort, new CaseInsensitiveComparer());
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < size; ++i)
            {
                sb.Append(arrayToSort[i]);
            }

            string signText = sb.Append("key=").Append(appSecret).ToString();
            return signText;
        }

        public string getObjString(Object obj)
        {
            return obj == null ? "" : (string)obj;
        }

        private string getValue(Object value)
        {
            return value is string ? getObjString(value) : treeJsonParam(value);
        }

        private static string treeJsonParam(Object value)
        {
            string jsonParam = null;
            if (value is Dictionary<Object, Object>)
            {
                SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                Dictionary<Object, Object> nestedMap = (Dictionary<Object, Object>)value;

                foreach (var entry in nestedMap)
                {
                    treeNestedMap.Add(entry.Key.ToString(), entry.Value);
                }
                jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None);
            }
            else if (value is List<Object>)
            {
                List<Object> ar = (List<Object>)value;
                jsonParam = JsonConvert.SerializeObject(treeList(ar), Formatting.None);
            }
            else if (value is JObject)
            {
                SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                JObject nestedMap = (JObject)value;
                foreach (var entry in nestedMap)
                {
                    treeNestedMap.Add(entry.Key.ToString(), entry.Value);
                }
                jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None);
            }
            else if (value is JArray)
            {
                JArray jarr = (JArray)value;
                jsonParam = JsonConvert.SerializeObject(treeJsonArray(jarr), Formatting.None);
            }
            else if (value is JValue)
            {
                JValue jval = (JValue)value;
                if (jval != null)
                    jsonParam = jval.Value.ToString();

            }
            else if (value is JProperty)
            {
                SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                JProperty nestedMap = (JProperty)value;
                treeNestedMap.Add(nestedMap.Name, nestedMap.Value);
                jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None);
            }
            else
            {
                jsonParam = value.ToString();
            }

            return jsonParam;
        }

        private static SortedDictionary<string, Object> treeParams(SortedDictionary<string, Object> param)
        {
            if (param == null)
            {
                return new SortedDictionary<string, Object>(StringComparer.Ordinal);
            }
            else
            {
                SortedDictionary<string, Object> treeParam = new SortedDictionary<string, Object>(StringComparer.Ordinal);

                while (true)
                {
                    foreach (var entry in param)
                    {

                        string key = (string)entry.Key;
                        Object value = entry.Value;
                        if (value is Dictionary<Object, Object>)
                        {
                            SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                            Dictionary<Object, Object> nestedMap = (Dictionary<Object, Object>)value;

                            foreach (var nestedEntry in nestedMap)
                            {
                                treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value);
                            }

                            treeParam.Add(key, treeParams(treeNestedMap));
                        }
                        else if (value is List<Object>)
                        {
                            List<Object> ar = (List<Object>)value;
                            treeParam.Add(key, treeList(ar));
                        }
                        else if (value is JArray)
                        {
                            JArray ar = (JArray)value;
                            treeParam.Add(key, treeJsonArray(ar));
                        }
                        else if (value is JObject)
                        {
                            SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                            JObject nestedMap = (JObject)value;
                            foreach (var nestedEntry in nestedMap)
                            {
                                treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value);
                            }
                            treeParam.Add(key, treeParams(treeNestedMap));
                        }
                        else if (value is JValue)
                        {
                            JValue jval = (JValue)value;
                            if (jval != null && !string.IsNullOrEmpty(jval.ToString()))
                                treeParam.Add(key, jval.ToString());
                        }
                        else if (value is JProperty)
                        {
                            SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                            JProperty nestedMap = (JProperty)value;
                            treeNestedMap.Add(nestedMap.Name, nestedMap.Value);
                            treeParam.Add(key, treeParams(treeNestedMap));
                        }
                        else if (!"".Equals(value) && value != null)
                        {
                            treeParam.Add(key, value.ToString());
                        }
                    }
                    return treeParam;
                }
            }
        }

        private static List<Object> treeList(List<Object> list)
        {
            if (list != null && list.Count() != 0)
            {
                JArray jsonArray = new JArray();
                int size = list.Count();

                for (int i = 0; i < size; ++i)
                {
                    jsonArray.Add(list[i]);
                }

                return treeJsonArray(jsonArray);
            }
            else
            {
                return null;
            }
        }

        private static List<Object> treeJsonArray(JArray jarr)
        {
            if (jarr != null && jarr.Count() != 0)
            {
                List<Object> jsonArray = new List<Object>();
                int size = jarr.Count();

                for (int i = 0; i < size; ++i)
                {
                    Object value = jarr[i];
                    if (value is List<Object>)
                    {
                        List<Object> ar = (List<Object>)value;
                        jsonArray.Add(treeList(ar));
                    }
                    else if (value is JArray)
                    {
                        JArray ar = (JArray)value;
                        jsonArray.Add(treeJsonArray(ar));
                    }
                    else if (value is JObject)
                    {
                        SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                        JObject nestedMap = (JObject)value;
                        foreach (var nestedEntry in nestedMap)
                        {
                            treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value);
                        }
                        jsonArray.Add(treeParams(treeNestedMap));

                    }
                    else if (value is JValue)
                    {
                        JValue jval = (JValue)value;
                        if (jval != null && !string.IsNullOrEmpty(jval.ToString()))
                            jsonArray.Add(jval.ToString());
                    }
                    else if (value is JProperty)
                    {
                        SortedDictionary<string, Object> treeNestedMap = new SortedDictionary<string, Object>(StringComparer.Ordinal);
                        JProperty nestedMap = (JProperty)value;
                        treeNestedMap.Add(nestedMap.Name, nestedMap.Value);
                        jsonArray.Add(treeParams(treeNestedMap));
                    }
                    else if (!"".Equals(value))
                    {
                        jsonArray.Add(value.ToString());
                    }

                }

                return jsonArray;
            }
            else
            {
                return null;
            }
        }
    }
}
