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

namespace Apewer.Source
{

    /// <summary>数据库中的列,类型默认为 NVarChar(191),错误类型将修正为默认类型。</summary>
    /// <remarks>注意:当一个数据模型中存在多个相同的 Field 时,将只有第一个被保留。</remarks>
    [Serializable]
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class ColumnAttribute : Attribute
    {

        private PropertyInfo _property = null;
        internal string PropertyName = null;

        private string _field = null;
        private int _length = 0;
        private ColumnType _type;

        private bool _independent = false;
        private bool _valid = true;

        private void Init(string field, ColumnType type, int length)
        {
            _field = field;
            _type = type;
            switch (type)
            {
                case ColumnType.VarChar:
                case ColumnType.NVarChar:
                    _length = length < 1 ? 191 : length;
                    break;
                case ColumnType.VarChar191:
                case ColumnType.NVarChar191:
                    _length = 191;
                    break;
                default:
                    _length = length;
                    break;
            }
        }

        /// <summary>使用自动的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        public ColumnAttribute(ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(null, type, length);

        /// <summary>使用指定的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        public ColumnAttribute(string field, ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(field, type, length);

        /// <summary>字段名。</summary>
        public string Field { get => _field; }

        /// <summary>指定字段的最大长度。</summary>
        public int Length { get => _length; }

        /// <summary>字段类型。</summary>
        public ColumnType Type { get => _type; }

        #region 附加

        /// <summary>此特性有效。</summary>
        public bool Valid { get => _valid; }

        /// <summary>Independent 特性。</summary>
        public bool Independent { get => _independent; }

        /// <summary>使用此特性的属性。</summary>
        public PropertyInfo Property { get => _property; }

        #endregion

        /// <summary>解析列特性。</summary>
        /// <remarks>注意:此方法不再抛出异常,当不存在正确的列特性时将返回 NULL 值</remarks>
        public static ColumnAttribute Parse(Type type, PropertyInfo property, TableAttribute ta)
        {
            if (type == null || property == null || ta == null) return null;

            // 属性带有 Independent 特性。
            if (property.Contains<IndependentAttribute>()) return null;

            // 检查 ColumnAttribute。
            ColumnAttribute ca;
            {
                var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
                if (cas.LongLength < 1L)
                {
                    if (!ta.AllProperties) return null;
                    ca = new ColumnAttribute();
                }
                else ca = (ColumnAttribute)cas[0];
            }

            // 检查属性方法。
            var getter = property.GetGetMethod(false);
            var setter = property.GetSetMethod(false);
            if (getter == null || getter.IsStatic) return null;
            if (setter == null || setter.IsStatic) return null;

            // 检查列名称。
            if (TextUtility.IsBlank(ca.Field)) ca._field = property.Name;

            // 类型兼容。
            var pt = property.PropertyType;
            if (pt.Equals(typeof(byte[]).FullName)) ca._type = ColumnType.Bytes;
            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.Bytes:
                    case ColumnType.Integer:
                    case ColumnType.Float:
                    case ColumnType.DateTime:
                        //throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
                        ca._type = ColumnType.NVarChar;
                        ca._length = 191;
                        break;
                }
            }
            else
            {
                ca._type = ColumnType.NVarChar191;
                ca._length = 191;
            }

            ca._property = property;
            ca.PropertyName = property.Name;
            if (ca.PropertyName == "Key" || ca.PropertyName == "Flag") ca._independent = true;

            return ca;
        }

        /// <summary>对列特性排序,Key 和 Flag 将始终排在前部。</summary>
        public static ColumnAttribute[] Sort(ColumnAttribute[] columns, bool sort = false)
        {
            var total = columns.Length;
            var key = null as ColumnAttribute;
            var flag = null as ColumnAttribute;
            var temp = new List<ColumnAttribute>(total);
            for (var i = 0; i < total; i++)
            {
                var ca = columns[i];
                if (ca == null) continue;
                var pn = ca.Property.Name;
                if (pn == "Key") key = ca;
                else if (pn == "Flag") flag = ca;
                else temp.Add(ca);
            }
            if (sort && temp.Count > 0) temp.Sort((a, b) => a._field.CompareTo(b._field));

            if (key == null && flag == null) return temp.ToArray();

            total = 0;
            if (key != null) total += 1;
            if (flag != null) total += 1;
            total += temp.Count;
            var sorted = new List<ColumnAttribute>(total);
            if (key != null) sorted.Add(key);
            if (flag != null) sorted.Add(flag);
            sorted.AddRange(temp);
            return sorted.ToArray();
        }

    }

}