﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using CordSharpCore.Document;
using Newtonsoft.Json.Serialization;

namespace CordSharpCore
{
    [AttributeUsage(AttributeTargets.Method)]
    public class ScriptCallableAttribute : Attribute
    {
        public Type CallerType { get; set; }

        public ScriptCallableAttribute(Type type)
        {
            CallerType = type;
        }
    }

    public abstract class ScriptCaller
    {
        private static JsonSerializerSettings _settings;

        public Delegate Delegate { get; set; }
        public abstract void Call(object target, params object[] args);
        public abstract void CreateDelegate(MethodInfo methodInfo);

        public ScriptCaller()
        {
            _settings = new JsonSerializerSettings { ContractResolver = new JsonResolver() };
        }

        public class JsonResolver : DefaultContractResolver
        {
            public override JsonContract ResolveContract(Type type)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition()
                                  == typeof(ObservableCollection<>))
                {
                    var c = (JsonArrayContract)
                              base.ResolveContract(typeof(List<>)
                                  .MakeGenericType(type.GetGenericArguments()[0]));
                    c.DefaultCreator = () => Activator.CreateInstance(type);
                    return c;
                }
                return base.ResolveContract(type);
            }
        }

        public static object ConvertValue(object value, Type type)
        {
            if (type == typeof(bool))
            {
                if (value.ToString().ToLower() == "false")
                {
                    return false;
                }
                if (value.ToString().ToLower() == "true")
                {
                    return true;
                }
            }
            else if (type == typeof(int))
            {
                int intResult = 0;
                if (int.TryParse(value.ToString(), out intResult))
                {
                    return intResult;
                }
            }
            else if (type == typeof(double))
            {
                double doubleResult = 0;
                if (double.TryParse(value.ToString(), out doubleResult))
                {
                    return doubleResult;
                }
            }
            else if (type == typeof(string))
            {
                return value.ToString();
            }
            else if (type.IsEnum)
            {
                return System.Enum.Parse(type, value.ToString(), false);
            }
            return JsonConvert.DeserializeObject(value.ToString(), type, _settings);
        }

    }
    public class ScriptCaller<T> : ScriptCaller
    {
        public override void Call(object target, params object[] args)
        {
            var action = Delegate as Action<T>;
            action((T)target);
        }

        public override void CreateDelegate(MethodInfo methodInfo)
        {
            Delegate = Delegate.CreateDelegate(typeof(Action<T>), methodInfo);
        }
    }
    public class ScriptCaller<T, T2> : ScriptCaller
    {
        public override void Call(object target, params object[] args)
        {
            var action = Delegate as Action<T, T2>;
            action((T)target, (T2)ScriptCaller.ConvertValue(args[0], typeof(T2)));
        }
        public override void CreateDelegate(MethodInfo methodInfo)
        {
            Delegate = Delegate.CreateDelegate(typeof(Action<T, T2>), methodInfo);
        }
    }
    public class ScriptCaller<T, T2, T3> : ScriptCaller<T>
    {
        public override void Call(object target, params object[] args)
        {
            var action = Delegate as Action<T, T2, T3>;
            action((T)target, (T2)ScriptCaller.ConvertValue(args[0], typeof(T2)), (T3)ScriptCaller.ConvertValue(args[1], typeof(T3)));
        }

        public override void CreateDelegate(MethodInfo methodInfo)
        {
            Delegate = Delegate.CreateDelegate(typeof(Action<T, T2, T3>), methodInfo);
        }
    }

    public class HtmlElementEventCaller<T> : ScriptCaller<T>
    {
        public override void Call(object target, params object[] args)
        {
            var action = Delegate as Action<T, object, HtmlElementEventArgs>;
            action((T)target, null, new HtmlElementEventArgs());
        }

        public override void CreateDelegate(MethodInfo methodInfo)
        {
            Delegate = Delegate.CreateDelegate(typeof(Action<T, object, HtmlElementEventArgs>), methodInfo);
        }
    }

}
