using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using DapperExtensions.Mapper;
using DapperExtensions.Sql;
using Mall.DataAccess;

namespace DapperExtensions
{
    /// <summary>
    /// Dapper扩展配置接口
    /// </summary>
    public interface IDapperExtensionConfiguration
    {
        /// <summary>
        /// 默认类型
        /// </summary>
        Type DefaultMapper { get; }

        /// <summary>
        /// 程序集列表对象
        /// </summary>
        IList<Assembly> MappingAssemblies { get; }

        /// <summary>
        /// Sql语句配置接口
        /// </summary>
        ISqlDialect Dialect { get; }

        /// <summary>
        /// 数据库配置接口
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns></returns>
        IClassMapper GetMap(Type entityType);

        /// <summary>
        /// 数据库配置接口
        /// </summary>
        /// <typeparam name="T">泛型约束</typeparam>
        /// <returns></returns>
        IClassMapper GetMap<T>() where T : class;

        /// <summary>
        /// 清除缓存
        /// </summary>
        void ClearCache();

        /// <summary>
        /// 获取GUID
        /// </summary>
        /// <returns></returns>
        Guid GetNextGuid();
    }

    /// <summary>
    /// Dapper扩展帮助类
    /// </summary>
    public class DapperExtensionConfiguration : IDapperExtensionConfiguration
    {
        private readonly ConcurrentDictionary<Type, IClassMapper> _classMaps = new ConcurrentDictionary<Type, IClassMapper>();

        /// <summary>
        /// 构造函数
        /// </summary>
        public DapperExtensionConfiguration(): this(typeof(AutoClassMapper<>), new List<Assembly>(), new SqlServerDialect())
        {
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="defaultMapper">默认映射</param>
        /// <param name="mappingAssemblies">程序集列表对象</param>
        /// <param name="sqlDialect">sql语句配置对象</param>
        public DapperExtensionConfiguration(Type defaultMapper, IList<Assembly> mappingAssemblies, ISqlDialect sqlDialect)
        {
            this.DefaultMapper = defaultMapper;
            this.MappingAssemblies = mappingAssemblies ?? new List<Assembly>();
            this.Dialect = sqlDialect;
        }

        /// <summary>
        /// 默认映射
        /// </summary>
        public Type DefaultMapper { get; private set; }

        /// <summary>
        /// 程序集列表对象
        /// </summary>
        public IList<Assembly> MappingAssemblies { get; private set; }

        /// <summary>
        /// sql语句配置对象
        /// </summary>
        public ISqlDialect Dialect { get; private set; }


        /// <summary>
        /// 获取数据库配置接口
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns></returns>
        public IClassMapper GetMap(Type entityType)
        {
            IClassMapper map;
            if (!_classMaps.TryGetValue(entityType, out map))
            {
                Type mapType = GetMapType(entityType);
                if (mapType == null)
                {
                    mapType = DefaultMapper.MakeGenericType(entityType);
                }

                map = Activator.CreateInstance(mapType) as IClassMapper;
                _classMaps[entityType] = map;
            }
            return map;
        }

        /// <summary>
        /// 获取数据库配置接口
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <returns></returns>
        public IClassMapper GetMap<T>() where T : class
        {
            return GetMap(typeof(T));
        }

        /// <summary>
        /// 清空缓存
        /// </summary>
        public void ClearCache()
        {
            _classMaps.Clear();
        }

        /// <summary>
        /// 获取GUID
        /// </summary>
        /// <returns></returns>
        public Guid GetNextGuid()
        {
            byte[] b = Guid.NewGuid().ToByteArray();
            DateTime dateTime = new DateTime(1900, 1, 1);
            DateTime now = DateTime.Now;
            TimeSpan timeSpan = new TimeSpan(now.Ticks - dateTime.Ticks);
            TimeSpan timeOfDay = now.TimeOfDay;
            byte[] bytes1 = BitConverter.GetBytes(timeSpan.Days);
            byte[] bytes2 = BitConverter.GetBytes((long)(timeOfDay.TotalMilliseconds / 3.333333));
            Array.Reverse(bytes1);
            Array.Reverse(bytes2);
            Array.Copy(bytes1, bytes1.Length - 2, b, b.Length - 6, 2);
            Array.Copy(bytes2, bytes2.Length - 4, b, b.Length - 4, 4);
            return new Guid(b);
        }

        /// <summary>
        /// 获取映射类型
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns></returns>
        protected virtual Type GetMapType(Type entityType)
        {
            Func<Assembly, Type> getType = a =>
            {
                Type[] types = a.GetTypes();
                return (from type in types
                        let interfaceType = type.GetInterface(typeof(IClassMapper<>).FullName)
                        where
                            interfaceType != null &&
                            interfaceType.GetGenericArguments()[0] == entityType
                        select type).FirstOrDefault();
            };
            Type result = getType(entityType.Assembly);
            if (result != null)
            {
                return result;
            }
            foreach (var mappingAssembly in MappingAssemblies)
            {
                result = getType(mappingAssembly);
                if (result != null)
                {
                    return result;
                }
            }
            return getType(entityType.Assembly);
        }
    }
}