using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using VIZ.Framework.Core;

namespace VIZ.Framework.Storage
{
    /// <summary>
    /// Ini仓储
    /// </summary>
    public class IniStorage
    {
        /// <summary>
        /// 每次读取最大值
        /// </summary>
        public const int MAX_READ_SIZE = 255;

        /// <summary>
        /// 声明INI文件的写操作函数 
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="key">键</param>
        /// <param name="val">值</param>
        /// <param name="filePath">Ini文件路径</param>
        /// <returns></returns>
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

        /// <summary>
        /// 声明INI文件的读操作函数 
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="key">键</param>
        /// <param name="def">默认值</param>
        /// <param name="retVal">返回值</param>
        /// <param name="size">目标缓存器大小</param>
        /// <param name="filePath">Ini文件路径</param>
        /// <returns></returns>
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string def, System.Text.StringBuilder retVal, int size, string filePath);

        private string sPath = null;

        /// <summary>
        /// Ini文件仓储
        /// </summary>
        /// <param name="path">Ini文件路径</param>
        public IniStorage(string path)
        {
            this.sPath = path;
        }

        /// <summary>
        /// 设置值
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        public void SetValue(string section, string key, string value)
        {
            // section=配置节，key=键名，value=键值，path=路径
            WritePrivateProfileString(section, key, value, sPath);
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="section">配置节</param>
        /// <param name="key">键</param>
        /// <returns>值</returns>
        public string GetValue(string section, string key)
        {
            // 每次从ini中读取多少字节
            StringBuilder temp = new StringBuilder(MAX_READ_SIZE);
            // section=配置节，key=键名，temp=上面，path=路径
            GetPrivateProfileString(section, key, string.Empty, temp, MAX_READ_SIZE, sPath);
            return temp.ToString();
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <typeparam name="TConfig">配置对象类型</typeparam>
        /// <typeparam name="TValue">配置值类型</typeparam>
        /// <typeparam name="TProperty">属性类型</typeparam>
        /// <param name="property">属性选择器</param>
        /// <returns>配置值</returns>
        public TValue GetValue<TConfig, TValue>(Expression<Func<TConfig, string>> property) where TConfig : IniConfigBase
        {
            Type type = typeof(TConfig);

            string str_property = property.ToString();
            string propertyName = str_property.Substring(str_property.IndexOf('.') + 1);

            PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo == null)
                throw new Exception($"{str_property} is not found.");

            IniAttribute attribute = propertyInfo.GetCustomAttribute<IniAttribute>(true);
            if (attribute == null)
                throw new Exception($"{str_property} no IniAttribute.");

            // 每次从ini中读取多少字节
            StringBuilder temp = new StringBuilder(MAX_READ_SIZE);
            // section=配置节，key=键名，temp=上面，path=路径
            GetPrivateProfileString(attribute.Section, propertyName, attribute.DefaultValue, temp, MAX_READ_SIZE, sPath);

            TValue result = this.ConvertValue<TValue>(temp?.ToString());

            return result;
        }

        /// <summary>
        /// 设置值
        /// </summary>
        /// <typeparam name="T">配置对象类型</typeparam>
        /// <param name="property">属性选择器</param>
        /// <param name="value">属性值</param>
        public void SetValue<T>(Expression<Func<T, string>> property, object value) where T : IniConfigBase
        {
            Type type = typeof(T);
            string str_property = property.ToString();
            string propertyName = str_property.Substring(str_property.IndexOf('.') + 1);

            PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo == null)
                throw new Exception($"{str_property} is not found.");

            IniAttribute attribute = propertyInfo.GetCustomAttribute<IniAttribute>(true);
            if (attribute == null)
                throw new Exception($"{str_property} no IniAttribute.");

            // section=配置节，key=键名，value=键值，path=路径
            WritePrivateProfileString(attribute.Section, propertyName, (value == null ? string.Empty : value.ToString()), sPath);
        }

        /// <summary>
        /// 转化类型
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private TValue ConvertValue<TValue>(string value)
        {
            if (string.IsNullOrEmpty(value))
                return default;

            Type type = typeof(TValue);

            // String
            if (type == typeof(string))
                return (TValue)(object)value;

            // Int32
            if (type == typeof(Int32))
                return (TValue)(object)Convert.ToInt32(value);

            // Int64
            if (type == typeof(Int64))
                return (TValue)(object)Convert.ToInt64(value);

            // Float
            if (type == typeof(float))
                return (TValue)(object)(float)Convert.ToDouble(value);

            // Double
            if (type == typeof(double))
                return (TValue)(object)Convert.ToDouble(value);

            // Bool
            if (type == typeof(bool))
                return (TValue)(object)Convert.ToBoolean(value);

            // RawColor4
            if (type == typeof(RawColor4))
                return (TValue)(object)SharpDxColorHelper.FromString(value);

            return default;
        }
    }
}
