using Apewer.Internals;
using Apewer;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;

namespace Apewer.Source
{

    /// <summary></summary>
    [Serializable]
    public sealed class TableStructure
    {

        private bool _locked = false;

        private string _tablename = Constant.EmptyString;
        private bool _independent = false;

        private Dictionary<string, ColumnAttribute> _columns = new Dictionary<string, ColumnAttribute>();

        internal TableStructure() { }

        /// <summary>不依赖 Record 公共属性。</summary>
        public bool Independent
        {
            get { return _independent; }
            private set { _independent = value; }
        }

        /// <summary>表名称。</summary>
        public string Table
        {
            get { return _tablename; }
            private set { _tablename = value ?? ""; }
        }

        /// <summary>列信息。</summary>
        public Dictionary<string, ColumnAttribute> Columns
        {
            get
            {
                if (_locked)
                {
                    var copy = new Dictionary<string, ColumnAttribute>(_columns.Count);
                    foreach (var c in _columns) copy.Add(c.Key, c.Value);
                    return copy;
                }
                return _columns;
            }
            private set { _columns = value; }
        }

        /// <summary>锁定属性,阻止修改。</summary>
        public void Lock()
        {
            _locked = true;
            foreach (var c in _columns) c.Value.Lock();
        }

        #region cache

        private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();

        private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();

        #endregion

        #region static

        /// <summary></summary>
        /// <exception cref="System.Exception"></exception>
        /// <exception cref="System.ArgumentNullException"></exception>
        public static TableStructure ParseModel(object entity, bool useCache = true)
        {
            if (entity == null) throw new ArgumentNullException("参数无效");
            var type = entity.GetType();
            var result = ParseModel(type, useCache);
            return result;
        }

        /// <summary></summary>
        /// <exception cref="System.Exception"></exception>
        /// <exception cref="System.ArgumentNullException"></exception>
        public static TableStructure ParseModel<T>(bool useCache = true) where T : IRecord
        {
            return ParseModel(typeof(T), useCache);
        }

        /// <summary></summary>
        /// <exception cref="System.Exception"></exception>
        /// <exception cref="System.ArgumentNullException"></exception>
        public static TableStructure ParseModel(Type model, bool useCache = true)
        {
            var type = model;
            if (type == null) throw new ArgumentNullException("参数无效");

            // 使用缓存。
            var cacheKey = type.FullName;
            if (useCache)
            {
                var hint = null as TableStructure;
                lock (_tsc)
                {
                    if (_tsc.ContainsKey(cacheKey))
                    {
                        hint = _tsc[cacheKey];
                    }
                }
                if (hint != null) return hint;
            }

            // 检查基类。
            // if (type.BaseType.FullName.Equals(typeof(DatabaseRecord).FullName) == false) return "基类不受支持。";

            // 检查 Attribute。
            var ta = ParseTable(type);

            // 获取所有属性。
            var properties = type.GetProperties();
            if (properties.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含属性。"));

            // Record 根类属性名。
            var roots = GetRootProperties();

            // 检查字段定义。键:属性名称。
            var columns = new Dictionary<string, ColumnAttribute>();
            foreach (var property in properties)
            {
                var ca = ParseColumn(type, property);
                if (ca == null) continue;

                // 检查冗余。
                foreach (var column in columns)
                {
                    if (column.Value.Field == ca.Field)
                    {
                        throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的列名称存在冗余。"));
                    }
                }

                // 检查基类。
                if (roots.Contains(ca.Property.Name)) ca.Independent = true;

                columns.Add(property.Name, ca);
            }
            // if (columns.Count < 1) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含可用的列。"));

            // 排序。
            columns = SortColumns(columns);

            // 返回结果。
            var ts = new TableStructure();
            ts.Table = ta.Name;
            ts.Independent = ta.Independent;
            ts.Columns = columns;

            // 锁定属性。
            ts.Lock();

            // 加入缓存。
            if (useCache)
            {
                lock (_tsc)
                {
                    if (!_tsc.ContainsKey(cacheKey))
                    {
                        _tsc.Add(cacheKey, ts);
                    }
                }
            }

            return ts;
        }

        /// <summary></summary>
        /// <param name="type"></param>
        /// <exception cref="Exception"></exception>"
        public static TableAttribute ParseTable<T>(bool useCache = true) where T : IRecord
        {
            return ParseTable(typeof(T), useCache);
        }

        /// <summary></summary>
        /// <param name="type"></param>
        /// <exception cref="Exception"></exception>"
        public static TableAttribute ParseTable(Type type, bool useCache = true)
        {
            // 使用缓存。
            var cacheKey = type.FullName;
            if (useCache)
            {
                var hint = null as TableAttribute;
                lock (_tac)
                {
                    if (_tac.ContainsKey(cacheKey))
                    {
                        hint = _tac[cacheKey];
                    }
                }
                if (hint != null) return hint;
            }

            var tas = type.GetCustomAttributes(typeof(TableAttribute), false);
            if (tas.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含 ", typeof(TableAttribute).FullName, "。"));
            if (tas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 包含多个 ", typeof(TableAttribute).FullName, "。"));

            var ta = (TableAttribute)tas[0];
            if (TextVerifier.IsBlank(ta.Name))
            {
                ta = new TableAttribute("_" + type.Name);
                if (TextVerifier.IsBlank(ta.Name)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 的表名称无效。"));
            }

            ta.Independent = ClassUtility.ContainsAttribute<IndependentAttribute>(type, true);

            // 锁定属性。
            ta.Lock();

            // 加入缓存。
            if (useCache)
            {
                lock (_tac)
                {
                    if (!_tac.ContainsKey(cacheKey))
                    {
                        _tac.Add(cacheKey, ta);
                    }
                }
            }

            return ta;
        }

        /// <summary></summary>
        /// <exception cref="Exception">Exception</exception>"
        public static ColumnAttribute ParseColumn(Type type, PropertyInfo property)
        {
            // 检查 Attributes。
            var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
            if (cas.LongLength < 1L) return null;
            if (cas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 包含多个 ", typeof(ColumnAttribute).FullName, "。"));
            var ca = (ColumnAttribute)cas[0];

            // 检查列名称。
            if (TextVerifier.IsBlank(ca.Field))
            {
                ca = new ColumnAttribute("_" + property.Name, ca.Type, ca.Length, true);
                if (TextVerifier.IsBlank(ca.Field)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, "中,属性 ", property.Name, " 的列名称无效。"));
            }

            // 检查属性方法。
            var getter = property.GetGetMethod(false);
            var setter = property.GetSetMethod(false);
            if (getter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持获取。"));
            if (setter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持设置。"));

            // 类型兼容。
            var pt = property.PropertyType;
            if (pt.Equals(typeof(byte[]).FullName)) ca.Type = ColumnType.Binary;
            else if (pt.Equals(typeof(Byte))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(SByte))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(Int16))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(UInt16))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(Int32))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(UInt32))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(Int64))) ca.Type = ColumnType.Integer;
            else if (pt.Equals(typeof(Single))) ca.Type = ColumnType.Float;
            else if (pt.Equals(typeof(Double))) ca.Type = ColumnType.Float;
            else if (pt.Equals(typeof(Decimal))) ca.Type = ColumnType.Float;
            else if (pt.Equals(typeof(DateTime))) ca.Type = ColumnType.DateTime;
            else if (pt.Equals(typeof(String)))
            {
                switch (ca.Type)
                {
                    case ColumnType.Binary:
                    case ColumnType.Integer:
                    case ColumnType.Float:
                    case ColumnType.DateTime:
                        //throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
                        ca.Type = ColumnType.NText;
                        break;
                }
            }
            else
            {
                ca.Type = ColumnType.NText;
            }

            ca.Property = property;

            // 锁定属性。
            ca.Lock();

            return ca;
        }

        /// <summary>排序。</summary>
        public static Dictionary<string, ColumnAttribute> SortColumns(Dictionary<string, ColumnAttribute> columns)
        {
            // if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。

            var sorted = new Dictionary<string, ColumnAttribute>();

            if (columns.ContainsKey("Created")) sorted.Add("Created", columns["Created"]);
            if (columns.ContainsKey("Updated")) sorted.Add("Updated", columns["Updated"]);
            if (columns.ContainsKey("Flag")) sorted.Add("Flag", columns["Flag"]);
            if (columns.ContainsKey("Remark")) sorted.Add("Remark", columns["Remark"]);
            if (columns.ContainsKey("Key")) sorted.Add("Key", columns["Key"]);

            foreach (var property in columns.Keys)
            {
                if (property == "Created") continue;
                if (property == "Updated") continue;
                if (property == "Flag") continue;
                if (property == "Remark") continue;
                if (property == "Key") continue;

                sorted.Add(property, columns[property]);
            }

            return sorted;
        }

        /// <summary>限定表名称/列名称。</summary>
        public static string RestrictName(string name, bool underline)
        {
            if (name == null || name == Constant.EmptyString) return Constant.EmptyString;
            var lower = name.ToLower();
            var available = TextGenerator.Merge("_", Constant.NumberCollection, Constant.LowerCollection);
            var sb = new StringBuilder();
            foreach (var c in lower)
            {
                if (available.IndexOf(c) >= 0) sb.Append(c);
            }
            lower = sb.ToString();
            if (underline && !lower.StartsWith("_")) lower = TextGenerator.Merge("_", lower);
            while (lower.Length > 2 && lower.StartsWith("__")) lower = lower.Substring(1);
            if (lower == "_" || lower == Constant.EmptyString) return Constant.EmptyString;
            if (lower.Length > 255) lower = lower.Substring(0, 255);
            return lower;
        }

        private static IDataParameter GenerateDataParameter(Record entity, ColumnAttribute attribute, CreateDataParameterCallback callback)
        {
            var property = attribute.Property;
            if (property == null) return null;

            var getter = property.GetGetMethod();
            if (getter == null) return null;

            var parameter = null as IDataParameter;
            var value = getter.Invoke(entity, null);

            // 
            if (attribute.Type == ColumnType.Binary || attribute.Type == ColumnType.Integer || attribute.Type == ColumnType.Float)
            {
                var temp = value;
                if (property.PropertyType.FullName == typeof(Decimal).FullName)
                {
                    temp = TextUtility.GetDouble(temp.ToString());
                }
                parameter = callback(new Parameter(attribute.Field, temp, attribute.Type, attribute.Length));
            }
            else if (attribute.Type == ColumnType.DateTime)
            {
                parameter = callback(new Parameter(attribute.Field, value, attribute.Type, 0));
            }
            else if (property.PropertyType.Equals(typeof(String)))
            {
                var text = value as string;
                if (text == null) text = "";
                if (attribute.Length > 0)
                {
                    switch (attribute.Type)
                    {
                        case ColumnType.VarChar:
                        case ColumnType.NVarChar:
                            text = TextUtility.RestrictLength(text, attribute.Length);
                            break;
                        case ColumnType.VarChar255:
                        case ColumnType.NVarChar255:
                            text = TextUtility.RestrictLength(text, 255);
                            break;
                    }
                }
                parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
            }
            else
            {
                var text = (value == null) ? TextUtility.EmptyString : value.ToString();
                parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
            }
            return parameter;
        }

        /// <summary>生成 IDataParameter 列表,用于 Insert 或 Update。</summary>
        /// <exception cref="ArgumentNullException"></exception>
        public List<IDataParameter> CreateDataParameters(Record entity, CreateDataParameterCallback callback, params string[] excluded)
        {
            if (entity == null) throw new ArgumentNullException("argEntity");
            if (callback == null) throw new ArgumentNullException("argCallback");

            entity.FixProperties();

            var list = new List<IDataParameter>();
            foreach (var column in Columns)
            {
                var attribute = column.Value;
                if (ParseTable(entity.GetType()).Independent && attribute.Independent) continue;

                var parameter = GenerateDataParameter(entity, attribute, callback);
                if (parameter == null) continue;

                var add = true;
                foreach (var exclude in excluded)
                {
                    var lower = parameter.ParameterName.ToLower();
                    if (lower == exclude.ToLower())
                    {
                        add = false;
                    }
                }
                if (add) list.Add(parameter);
            }

            return list;
        }

        #endregion

        #region

        /// <summary>获取 Record 根类中的属性名称。</summary>
        private static List<string> GetRootProperties()
        {
            var list = new List<string>();
            var type = typeof(Record);
            var properties = type.GetProperties();
            foreach (var property in properties)
            {
                if (ClassUtility.ContainsAttribute<ColumnAttribute>(property, false))
                {
                    list.Add(property.Name);
                }
            }
            return list;
        }

        #endregion

    }

}