using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EzFramework
{
    internal enum ServicesMappingType
    {
        Singleton,
        Instance,
        Transient
    }
    internal class ServicesMapping
    {
        public ServicesMappingType MapType { set; get; }

        public Type TargetType { set; get; }

    }
    /// <summary>
    /// 用户服务接口
    /// </summary>
    public class UseServices
    {
        private static object _obj = new object();
        private static Hashtable cached = new Hashtable();
        private static IDictionary<int, ServicesMapping> maps = new Dictionary<int, ServicesMapping>();
        private static void TryCompatibilit(Type itype1, Type classType)
        {
            if (!itype1.IsAssignableFrom(classType))
            {
                throw new ArgumentException("The corresponding interface type cannot be instantiated!");
            }
        }
        /// <summary>
        /// 创建一个单例,以后每次调用的时候都返回该单例对象。
        /// </summary>
        /// <typeparam name="TTnterface">接口类型</typeparam>
        /// <typeparam name="Tclass">实现类类型</typeparam>
        public static void AddSingleton<TTnterface, Tclass>()
        {
            AddSingleton(typeof(TTnterface), typeof(Tclass));
        }
        /// <summary>
        /// 创建一个单例,以后每次调用的时候都返回该单例对象。
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <param name="classType">实现类类型</param>
        public static void AddSingleton(Type interfaceType, Type classType)
        {
            if (interfaceType == null || classType == null) throw new ArgumentNullException("params error");
            TryCompatibilit(interfaceType, classType);
            lock (_obj)
            {
                if (!maps.ContainsKey(interfaceType.GetHashCode())) maps.Add(interfaceType.GetHashCode(), new ServicesMapping
                {
                    MapType = ServicesMappingType.Singleton,
                    TargetType = classType
                });
            }
        }

        /// <summary>
        /// 任何时间都只能使用特定的实例对象,开发人员需要负责该对象的初始化工作。
        /// </summary>
        /// <typeparam name="TTnterface">接口类型</typeparam>
        /// <param name="instance">实例</param>
        public static void AddInstance<TTnterface>(object instance)
        {
            AddInstance(typeof(TTnterface), instance);

        }
        /// <summary>
        /// 任何时间都只能使用特定的实例对象,开发人员需要负责该对象的初始化工作。
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <param name="instance">实例</param>
        public static void AddInstance(Type interfaceType, object instance)
        {
            if (interfaceType == null || instance == null) throw new ArgumentNullException("params error");
            TryCompatibilit(interfaceType, instance.GetType());
            lock (_obj)
            {
                if (!cached.ContainsKey(interfaceType.GetHashCode())) cached.Add(interfaceType.GetHashCode(), instance);
                if (!maps.ContainsKey(interfaceType.GetHashCode())) maps.Add(interfaceType.GetHashCode(), new ServicesMapping
                {
                    MapType = ServicesMappingType.Instance,
                    TargetType = instance.GetType()
                });
            }
        }

        /// <summary>
        /// 每次都重新创建一个实例。
        /// </summary>
        /// <typeparam name="TTnterface">接口类型</typeparam>
        /// <typeparam name="Tclass">实现类类型</typeparam>
        public static void AddTransient<TTnterface, Tclass>()
        {
            AddTransient(typeof(TTnterface), typeof(Tclass));
        }
        /// <summary>
        /// 每次都重新创建一个实例。
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <param name="classType">实现类类型</param>
        public static void AddTransient(Type interfaceType, Type classType)
        {
            if (interfaceType == null || classType == null) throw new ArgumentNullException("params error");
            TryCompatibilit(interfaceType, classType);
            lock (_obj)
            {
                if (!maps.ContainsKey(interfaceType.GetHashCode())) maps.Add(interfaceType.GetHashCode(), new ServicesMapping
                {
                    MapType = ServicesMappingType.Transient,
                    TargetType = classType
                });
            }
        }
        
        /// <summary>
        /// 获取用户接口服务实例
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static object GetUseService(Type interfaceType)
        {
            if (interfaceType == null) throw new ArgumentNullException("params error");
            int identity = interfaceType.GetHashCode();
            if (maps.ContainsKey(identity))
            {
                ServicesMapping map = maps[identity];

                switch (map.MapType)
                {
                    case ServicesMappingType.Instance:
                        {
                            try
                            {
                                if (cached.ContainsKey(identity)) return identity;
                                return null;
                            }
                            catch (Exception exp)
                            {
                                throw new Exception("Type instantiation failed!" + exp.Message);
                            }
                        }
                    case ServicesMappingType.Singleton:
                        {
                            try
                            {
                                if (cached.ContainsKey(identity)) return identity;
                                object insatnce = Activator.CreateInstance(map.TargetType);
                                lock (_obj)
                                {
                                    if (cached.ContainsKey(identity)) return identity;
                                    cached.Add(identity, insatnce);
                                }
                            }
                            catch (Exception exp)
                            {
                                throw new Exception("Type instantiation failed!" + exp.Message);
                            }
                        }
                        break;
                    case ServicesMappingType.Transient:
                        {
                            try
                            {
                                return Activator.CreateInstance(map.TargetType);
                            }
                            catch (Exception exp)
                            {
                                throw new Exception("Type instantiation failed!" + exp.Message);
                            }
                        }
                }
            }
            throw new Exception("Type instantiation failed!");
        }
    }
}
