using DapperExtensions.Sql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;

namespace DapperExtensions.Mapper
{
    /// <summary>
    /// 属性映射接口
    /// Maps an entity property to its corresponding column in the database.
    /// 将实体属性映射到数据库中相应的列
    /// </summary>
    public interface IPropertyMap
    {
        /// <summary>
        /// 数据库名称
        /// </summary>
        string DbName { get; }

        /// <summary>
        /// 架构名称
        /// </summary>
        string SchemaName { get; }

        /// <summary>
        /// 表名称
        /// </summary>
        string TableName { get; }


        /// <summary>
        /// 名称
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 字段名称
        /// </summary>
        string ColumnName { get; }

        /// <summary>
        /// 是否忽略
        /// </summary>
        bool Ignored { get; }

        /// <summary>
        /// 是否只读
        /// </summary>
        bool IsReadOnly { get; }

        /// <summary>
        /// 字段类型
        /// </summary>
        KeyType KeyType { get; }

        /// <summary>
        /// 信息
        /// </summary>
        PropertyInfo PropertyInfo { get; }

    }

    /// <summary>
    /// Maps an entity property to its corresponding column in the database.
    /// 属性映射接口实现
    /// </summary>
    [Serializable]
    public class PropertyMap : IPropertyMap
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public PropertyMap() { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="pm">属性映射接口对象</param>
        public PropertyMap(IPropertyMap pm)
        {
            this.PropertyInfo = pm.PropertyInfo;
            this.Name = PropertyInfo.Name;
            this.TableName = pm.TableName;
            this.SchemaName = pm.SchemaName;
            this.ColumnName = pm.ColumnName;
            this.Ignored = pm.Ignored;
            this.IsReadOnly = pm.IsReadOnly;
            this.DbName = pm.DbName;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="entityType">实体类型</param>
        public PropertyMap(string key, Type entityType)
        {
            IClassMapper classMapper = DapperExtension.DapperImplementor.SqlGenerator.Configuration.GetMap(entityType);
            if (classMapper.Properties.ContainsKey(key))
            {
                IPropertyMap pm = classMapper.Properties[key];
                this.PropertyInfo = pm.PropertyInfo;
                this.Name = PropertyInfo.Name;
                this.TableName = pm.TableName;
                this.SchemaName = pm.SchemaName;
                this.ColumnName = pm.ColumnName;
                this.Ignored = pm.Ignored;
                this.IsReadOnly = pm.IsReadOnly;
                this.DbName = pm.DbName;
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">名称</param>
        /// <param name="tableName">表名称</param>
        /// <param name="dbName">数据库名称</param>
        /// <param name="schemaName">架构名称</param>
        /// <param name="columnName">列名</param>
        /// <param name="ignored">是否忽略</param>
        /// <param name="isReadOnly">是否只读</param>
        public PropertyMap(string name, string tableName = "", string dbName = "", string schemaName = "", string columnName = "", bool? ignored = null, bool? isReadOnly = null)
        {
            this.Name = name;
            this.TableName = tableName;
            this.DbName = dbName;
            this.SchemaName = schemaName;
            if (string.IsNullOrEmpty(columnName))
            {
                this.ColumnName = name;
            }
            else
            {
                this.ColumnName = columnName;
            }
            if (null != ignored)
            {
                this.Ignored = Convert.ToBoolean(ignored);
            }
            if (null != isReadOnly)
            {
                this.IsReadOnly = Convert.ToBoolean(isReadOnly);
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="propertyInfo">属性</param>
        /// <param name="tableName">表名</param>
        /// <param name="dbName">数据库名称</param>
        /// <param name="schemaName">架构名称</param>
        /// <param name="columnName">列名称</param>
        /// <param name="ignored">是否忽略</param>
        /// <param name="isReadOnly">是否只读</param>
        public PropertyMap(PropertyInfo propertyInfo, string tableName, string dbName = "", string schemaName = "", string columnName = "", bool? ignored = null, bool? isReadOnly = null)
        {
            this.PropertyInfo = propertyInfo;
            this.Name = PropertyInfo.Name;
            this.TableName = tableName;
            this.DbName = dbName;
            this.SchemaName = schemaName;
            if (string.IsNullOrEmpty(columnName))
            {
                this.ColumnName = PropertyInfo.Name;
            }
            else
            {
                this.ColumnName = columnName;
            }
            if (null != ignored)
            {
                this.Ignored = Convert.ToBoolean(ignored);
            }
            if (null != isReadOnly)
            {
                this.IsReadOnly = Convert.ToBoolean(isReadOnly);
            }
        }

        /// <summary>
        /// 数据库名称
        /// </summary>
        public string DbName { get; protected set; }

        /// <summary>
        /// 架构名称
        /// </summary>
        public string SchemaName { get; protected set; }

        /// <summary>
        /// 表名称
        /// </summary>
        public string TableName { get; protected set; }


        /// <summary>
        /// Gets the name of the property by using the specified propertyInfo.
        /// 通过使用指定的propertyInfo获取属性的名称。
        /// </summary>
        public string Name { get; protected set; }

        /// <summary>
        /// 列名称
        /// </summary>
        public string ColumnName { get; protected set; }

        /// <summary>
        /// Gets the key type for the current property.
        /// 获取当前属性的键类型。
        /// </summary>
        public KeyType KeyType { get; protected set; }

        /// <summary>
        /// 是否忽略
        /// </summary>
        public bool Ignored { get; protected set; }

        /// <summary>
        /// 是否只读
        /// </summary>
        public bool IsReadOnly { get; protected set; }

        /// <summary>
        /// 属性
        /// </summary>
        public PropertyInfo PropertyInfo { get; protected set; }

        /// <summary>
        /// 字段名称
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public PropertyMap Column(string columnName)
        {
            ColumnName = columnName;
            return this;
        }

        /// <summary>
        /// Fluently sets the key type of the property.
        /// </summary>
        /// <param name="keyType">The column name as it exists in the database.</param>
        public PropertyMap Key(KeyType keyType)
        {
            if (Ignored)
            {
                throw new ArgumentException(string.Format("'{0}' is ignored and cannot be made a key field. ", Name));
            }
            KeyType = keyType;
            return this;
        }

        /// <summary>
        /// Fluently sets the ignore status of the property.
        /// </summary>
        public PropertyMap Ignore()
        {
            if (KeyType != KeyType.NotAKey)
            {
                throw new ArgumentException(string.Format("'{0}' is a key field and cannot be ignored.", Name));
            }
            Ignored = true;
            return this;
        }

        /// <summary>
        /// 只读
        /// </summary>
        /// <returns></returns>
        public PropertyMap ReadOnly()
        {
            if (KeyType != KeyType.NotAKey)
            {
                throw new ArgumentException(string.Format("'{0}' is a key field and cannot be marked readonly.", Name));
            }
            IsReadOnly = true;
            return this;
        }

    }

    /// <summary>
    /// Used by ClassMapper to determine which entity property represents the key.
    /// </summary>
    public enum KeyType
    {
        /// <summary>
        /// The property is not a key and is not automatically managed.
        /// </summary>
        NotAKey,

        /// <summary>
        /// The property is an integery-based identity generated from the database.
        /// </summary>
        Identity,

        /// <summary>
        /// The property is a Guid identity which is automatically managed.
        /// </summary>
        Guid,

        /// <summary>
        /// The property is a key that is not automatically managed.
        /// </summary>
        Assigned
    }
}