using System;
using System.Linq.Expressions;
using System.Reflection;

namespace HslCommunication.Core
{
    /// <summary>
    /// 反射的辅助类
    /// </summary>
    public class HslReflectionHelper
    {

        /// <summary>
        /// 从设备里读取支持Hsl特性的数据内容,该特性为<see cref="HslDeviceAddressAttribute"/>,详细参考论坛的操作说明。
        /// </summary>
        /// <typeparam name="T">自定义的数据类型对象</typeparam>
        /// <param name="readWrite">读写接口的实现</param>
        /// <returns>包含是否成功的结果对象</returns>
        public static OperateResult<T> Read<T>(IReadWriteNet readWrite) where T : class, new()
        {
            var type = typeof(T);
            // var constrcuor = type.GetConstructors( System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic );
            var obj = type.Assembly.CreateInstance(type.FullName);

            var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            foreach (var property in properties)
            {
                var attribute = property.GetCustomAttributes(typeof(HslDeviceAddressAttribute), false);
                if (attribute == null) continue;

                HslDeviceAddressAttribute hslAttribute = null;
                for (int i = 0; i < attribute.Length; i++)
                {
                    HslDeviceAddressAttribute tmp = (HslDeviceAddressAttribute)attribute[i];
                    if (tmp.deviceType != null && tmp.deviceType == readWrite.GetType())
                    {
                        hslAttribute = tmp;
                        break;
                    }
                }

                if (hslAttribute == null)
                {
                    for (int i = 0; i < attribute.Length; i++)
                    {
                        HslDeviceAddressAttribute tmp = (HslDeviceAddressAttribute)attribute[i];
                        if (tmp.deviceType == null)
                        {
                            hslAttribute = tmp;
                            break;
                        }
                    }
                }

                if (hslAttribute == null) continue;

                Type propertyType = property.PropertyType;
                if (propertyType == typeof(short))
                {
                    OperateResult<short> valueResult = readWrite.ReadInt16(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(short[]))
                {
                    OperateResult<short[]> valueResult = readWrite.ReadInt16(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(ushort))
                {
                    OperateResult<ushort> valueResult = readWrite.ReadUInt16(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(ushort[]))
                {
                    OperateResult<ushort[]> valueResult = readWrite.ReadUInt16(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(int))
                {
                    OperateResult<int> valueResult = readWrite.ReadInt32(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(int[]))
                {
                    OperateResult<int[]> valueResult = readWrite.ReadInt32(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(uint))
                {
                    OperateResult<uint> valueResult = readWrite.ReadUInt32(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(uint[]))
                {
                    OperateResult<uint[]> valueResult = readWrite.ReadUInt32(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(long))
                {
                    OperateResult<long> valueResult = readWrite.ReadInt64(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(long[]))
                {
                    OperateResult<long[]> valueResult = readWrite.ReadInt64(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(ulong))
                {
                    OperateResult<ulong> valueResult = readWrite.ReadUInt64(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(ulong[]))
                {
                    OperateResult<ulong[]> valueResult = readWrite.ReadUInt64(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(float))
                {
                    OperateResult<float> valueResult = readWrite.ReadFloat(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(float[]))
                {
                    OperateResult<float[]> valueResult = readWrite.ReadFloat(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(double))
                {
                    OperateResult<double> valueResult = readWrite.ReadDouble(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(double[]))
                {
                    OperateResult<double[]> valueResult = readWrite.ReadDouble(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(string))
                {
                    OperateResult<string> valueResult = readWrite.ReadString(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(byte[]))
                {
                    OperateResult<byte[]> valueResult = readWrite.Read(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(bool))
                {
                    OperateResult<bool> valueResult = readWrite.ReadBool(hslAttribute.address);
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
                else if (propertyType == typeof(bool[]))
                {
                    OperateResult<bool[]> valueResult = readWrite.ReadBool(hslAttribute.address, (ushort)(hslAttribute.length > 0 ? hslAttribute.length : 1));
                    if (!valueResult.IsSuccess) return OperateResult.CreateFailedResult<T>(valueResult);

                    property.SetValue(obj, valueResult.Content, null);
                }
            }

            return OperateResult.CreateSuccessResult((T)obj);
        }


        /// <summary>
        /// 从设备里读取支持Hsl特性的数据内容,该特性为<see cref="HslDeviceAddressAttribute"/>,详细参考论坛的操作说明。
        /// </summary>
        /// <typeparam name="T">自定义的数据类型对象</typeparam>
        /// <param name="data">自定义的数据对象</param>
        /// <param name="readWrite">数据读写对象</param>
        /// <returns>包含是否成功的结果对象</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static OperateResult Write<T>(T data, IReadWriteNet readWrite) where T : class, new()
        {
            if (data == null) throw new ArgumentNullException(nameof(data));

            var type = typeof(T);
            var obj = data;

            var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            foreach (var property in properties)
            {
                var attribute = property.GetCustomAttributes(typeof(HslDeviceAddressAttribute), false);
                if (attribute == null) continue;

                HslDeviceAddressAttribute hslAttribute = null;
                for (int i = 0; i < attribute.Length; i++)
                {
                    HslDeviceAddressAttribute tmp = (HslDeviceAddressAttribute)attribute[i];
                    if (tmp.deviceType != null && tmp.deviceType == readWrite.GetType())
                    {
                        hslAttribute = tmp;
                        break;
                    }
                }

                if (hslAttribute == null)
                {
                    for (int i = 0; i < attribute.Length; i++)
                    {
                        HslDeviceAddressAttribute tmp = (HslDeviceAddressAttribute)attribute[i];
                        if (tmp.deviceType == null)
                        {
                            hslAttribute = tmp;
                            break;
                        }
                    }
                }

                if (hslAttribute == null) continue;


                Type propertyType = property.PropertyType;
                if (propertyType == typeof(short))
                {
                    short value = (short)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(short[]))
                {
                    short[] value = (short[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(ushort))
                {
                    ushort value = (ushort)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(ushort[]))
                {
                    ushort[] value = (ushort[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(int))
                {
                    int value = (int)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(int[]))
                {
                    int[] value = (int[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(uint))
                {
                    uint value = (uint)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(uint[]))
                {
                    uint[] value = (uint[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(long))
                {
                    long value = (long)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(long[]))
                {
                    long[] value = (long[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(ulong))
                {
                    ulong value = (ulong)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(ulong[]))
                {
                    ulong[] value = (ulong[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(float))
                {
                    float value = (float)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(float[]))
                {
                    float[] value = (float[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(double))
                {
                    double value = (double)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(double[]))
                {
                    double[] value = (double[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(string))
                {
                    string value = (string)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(byte[]))
                {
                    byte[] value = (byte[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(bool))
                {
                    bool value = (bool)property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
                else if (propertyType == typeof(bool[]))
                {
                    bool[] value = (bool[])property.GetValue(obj, null);

                    OperateResult writeResult = readWrite.Write(hslAttribute.address, value);
                    if (!writeResult.IsSuccess) return writeResult;
                }
            }

            return OperateResult.CreateSuccessResult((T)obj);
        }

        /// <summary>
        /// 使用表达式树的方式来给一个属性赋值
        /// </summary>
        /// <param name="propertyInfo">属性信息</param>
        /// <param name="obj">对象信息</param>
        /// <param name="objValue">实际的值</param>
        public static void SetPropertyExp<T, K>(PropertyInfo propertyInfo, T obj, K objValue)
        {
            // propertyInfo.SetValue( obj, objValue, null );  下面就是实现这句话
            var invokeObjExpr = Expression.Parameter(typeof(T), "obj");
            var propValExpr = Expression.Parameter(propertyInfo.PropertyType, "objValue");
            var setMethodExp = Expression.Call(invokeObjExpr, propertyInfo.GetSetMethod(), propValExpr);
            var lambda = Expression.Lambda<Action<T, K>>(setMethodExp, invokeObjExpr, propValExpr);
            lambda.Compile()(obj, objValue);
        }

    }
}