using System;
using System.Collections;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace CPF.Windows.Json.Serializer
{
    internal class SpecialConditions : DefaultJsonFormatter
    {
        #region pregenerated metas
        internal static MethodInfo _WriteEnum = typeof(SpecialConditions).GetMethod(nameof(WriteEnum), BindingFlags.NonPublic | BindingFlags.Static);
        #endregion

#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteDynamic(object value, JsonSerializerHandler handler)
        {
            if (value == null)
            {
                handler.WriteString("null");
                return;
            }

            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
            {
                if (handler.SerializeStacks.Contains(value))
                {
                    if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Null)
                        handler.WriteString("null");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Empty)
                        handler.WriteString("{}");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Remove)
                        RemoveWriterHelper.RemoveDictionaryKey(handler);
                    return;
                }
                handler.SerializeStacks.Push(value);
            }

            var t = value.GetType();
            handler.WriteString("{");
            bool isFirst = true;
            foreach (var item in t.GetProperties())
            {
                if (isFirst)
                    isFirst = false;
                else
                    handler.WriteString(",");
                handler.WriteString("\"");
                handler.WriteString(item.Name);
                handler.WriteString("\"");
                handler.WriteString(":");
#if Net4
                SerializerObjectJump.GetThreadSafetyJumpAction(item.PropertyType)(item.GetValue(value, null), handler);
#else
                SerializerObjectJump.GetThreadSafetyJumpAction(item.PropertyType)(item.GetValue(value), handler);
#endif
            }
            handler.WriteString("}");
        }

#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void AvoidTypes(object typeVar, JsonSerializerHandler handler)
        {
            handler.WriteString(typeVar == null ? "null" : "{}");
        }

#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteEnum(Enum value, JsonSerializerHandler handler)
        {
            if (handler.Option.IsEnumNum)
                PrimitiveNormal.WriteValue(value.GetHashCode(), handler);
            else
            {
                handler.WriteString("\"");
                handler.WriteString(value.ToString());
                handler.WriteString("\"");
            }
        }

        #region Array
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteCollection(IEnumerable value, JsonSerializerHandler handler)
        {
            if (value == null)
            {
                handler.WriteString("null");
                return;
            }

            var t = value.GetType();
            if (t.IsGenericType)
            {
                var serializerAction = SerializerObjectJump.GetThreadSafetyJumpAction(t);
                serializerAction(value, handler);
            }
            else
            {
                bool isMultidimensionalArray = false;
                Array ary = null;
                if (t.IsArray)
                {
                    ary = (Array)value;
                    if (ary.Rank > 1)
                        isMultidimensionalArray = true;
                }
                if (isMultidimensionalArray)
                    WriteMultidimensionalArray(ary, handler);
                else
                    WriteIEnumerable(value, handler);
            }
        }
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteIEnumerable(IEnumerable value, JsonSerializerHandler handler)
        {
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
            {
                if (handler.SerializeStacks.Contains(value))
                {
                    if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Null)
                        handler.WriteString("null");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Empty)
                        handler.WriteString("[]");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Remove)
                        RemoveWriterHelper.RemoveArrayItem(handler);
                    return;
                }
                handler.SerializeStacks.Push(value);
            }

            handler.WriteString("[");
            bool isFirst = true;
            foreach (var obj in value)
            {
                if (isFirst)
                    isFirst = false;
                else
                    handler.WriteString(",");
                PrimitiveNormal.WriteValue(obj, handler);
            }
            handler.WriteString("]");
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
                handler.SerializeStacks.Pop();
        }
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteMultidimensionalArray(Array value, JsonSerializerHandler handler)
        {
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
            {
                if (handler.SerializeStacks.Contains(value))
                {
                    if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Null)
                        handler.WriteString("null");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Empty)
                        handler.WriteString("[]");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Remove)
                        RemoveWriterHelper.RemoveArrayItem(handler);
                    return;
                }
                handler.SerializeStacks.Push(value);
            }
            var action = SerializerObjectJump.GetThreadSafetyJumpAction(value.GetType().GetElementType());
            handler.WriteString("[");
            var enumerator = value.GetEnumerator();
            MultidimensionalWrite(enumerator, handler, value, action, value.GetLength(0), 0);
            handler.WriteString("]");
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
                handler.SerializeStacks.Pop();
        }
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        private static void MultidimensionalWrite(IEnumerator enumerator, JsonSerializerHandler handler, Array value, Action<object, JsonSerializerHandler> action, int leng, int level)
        {
            for (int i = 0; i < leng; i++)
            {
                if (level != value.Rank - 1)
                    handler.WriteString("[");

                if (level == value.Rank - 1)
                {
                    enumerator.MoveNext();
                    action(enumerator.Current, handler);
                    if (i != leng - 1)
                        handler.WriteString(",");
                    continue;
                }

                int localLevel = level;
                ++localLevel;
                MultidimensionalWrite(enumerator, handler, value, action, value.GetLength(localLevel), localLevel);

                handler.WriteString("]");

                if (i != leng - 1)
                    handler.WriteString(",");
            }

        }
        #endregion

        #region Dictionary
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteDictionary(IDictionary value, JsonSerializerHandler handler)
        {
            if (value == null)
            {
                handler.WriteString("null");
                return;
            }
            var t = value.GetType();
            if (t.IsGenericType)
            {
                var serializerAction = SerializerObjectJump.GetThreadSafetyJumpAction(t);
                serializerAction(value, handler);
            }
            else
                WriteIDictionary(value, handler);
        }
#if !Net4
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        internal static void WriteIDictionary(IDictionary value, JsonSerializerHandler handler)
        {
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
            {
                if (handler.SerializeStacks.Contains(value))
                {
                    if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Null)
                        handler.WriteString("null");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Empty)
                        handler.WriteString("{}");
                    else if (handler.Option.ReferenceLoopHandling == JsonReferenceHandlingEnum.Remove)
                        RemoveWriterHelper.RemoveDictionaryKey(handler);
                    return;
                }
                handler.SerializeStacks.Push(value);
            }

            bool isFirst = true;
            handler.WriteString("{");
            foreach (DictionaryEntry item in value)
            {
                var keyType = item.Key.GetType();
                if (keyType.IsWrongKey())
                    continue;

                if (isFirst)
                    isFirst = false;
                else
                    handler.WriteString(",");

                if (keyType.IsPrimitive || (keyType.IsEnum && handler.Option.IsEnumNum))//string,primitive,enum,guid,datetime
                    handler.WriteString("\"");

                PrimitiveNormal.WriteValue(item.Key, handler);

                if (keyType.IsPrimitive || (keyType.IsEnum && handler.Option.IsEnumNum))
                    handler.WriteString("\"");

                handler.WriteString(":");
                PrimitiveNormal.WriteValue(item.Value, handler);
            }
            handler.WriteString("}");
            if (handler.Option.ReferenceLoopHandling != JsonReferenceHandlingEnum.None)
                handler.SerializeStacks.Pop();
        }
        #endregion

    }
}
