#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 命名空间:WIDESEAWCS_QuartzJob.DeviceBase
 * 创建者:胡童庆
 * 创建时间:2024/8/2 16:13:36
 * 版本:V1.0.0
 * 描述:
 *
 * ----------------------------------------------------------------
 * 修改人:
 * 修改时间:
 * 版本:V1.0.1
 * 修改说明:
 * 
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using HslCommunication;
using HslCommunication.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace WIDESEAWCS_QuartzJob.DeviceBase
{
    public class DeviceCommand : IDataTransfer
    {
        #region <Const>
        #endregion <Const>

        #region <Private Member>
        public ushort ReadCount { get; }

        private IByteTransform byteTransform = new ReverseBytesTransform();
        #endregion <Private Member>

        #region <Public Menber>

        #endregion <Public Menber>

        #region <Constructor function>
        public DeviceCommand()
        {
            ushort readCount = 0;
            PropertyInfo[] propertyInfos = GetType().GetProperties().Where(x => x.CanWrite).ToArray();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                object? obj = propertyInfo.GetValue(this);
                TypeCode typeCode = Type.GetTypeCode(propertyInfo.PropertyType);
                switch (typeCode)
                {
                    case TypeCode.Byte:
                    case TypeCode.SByte:
                    case TypeCode.Char:
                        readCount += sizeof(byte);
                        break;
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                        readCount += 2;
                        break;
                    case TypeCode.Single:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                        readCount += 4;
                        break;
                    case TypeCode.String:
                        ushort dataLength = CheckStringAttribute(propertyInfo);
                        readCount += dataLength;
                        break;
                    default:
                        throw new NotSupportedException("Unsupported primitive type: " + propertyInfo.PropertyType);
                }
            }
            ReadCount = readCount;
        }
        #endregion <Constructor function>

        #region <Private Method>
        private ushort CheckStringAttribute(PropertyInfo propertyInfo)
        {
            Attribute? attribute = propertyInfo.GetCustomAttribute(typeof(DataLengthAttribute));
            if (attribute == null)
            {
                throw new Exception($"字符串需要配置【DataLength】特性");
            }
            ushort dataLength = ((DataLengthAttribute)attribute).DataLength;
            if (dataLength <= 0 || dataLength > 256)
            {
                throw new Exception($"【DataLength】特性请配置有效参数,参数范围【1-256】");
            }
            if (dataLength % 2 != 0)
            {
                dataLength += 1;
            }
            dataLength += 2;

            return dataLength;
        }
        #endregion <Private Method>

        #region <Public Method>
        public void ParseSource(byte[] Content)
        {
            PropertyInfo[] propertyInfos = GetType().GetProperties().Where(x => x.CanWrite).ToArray();
            int index = 0;
            for (int i = 0; i < propertyInfos.Length; i++)
            {
                PropertyInfo propertyInfo = propertyInfos[i];

                TypeCode typeCode = Type.GetTypeCode(propertyInfo.PropertyType);
                switch (typeCode)
                {
                    case TypeCode.Byte:
                    case TypeCode.SByte:
                        propertyInfo.SetValue(this, Content[index]);
                        index += sizeof(byte);
                        break;
                    case TypeCode.Char:
                        propertyInfo.SetValue(this, (char)Content[index]);
                        index += sizeof(char);
                        break;
                    case TypeCode.Int16:
                        propertyInfo.SetValue(this, byteTransform.TransInt16(Content, index));
                        index += sizeof(short);
                        break;
                    case TypeCode.UInt16:
                        propertyInfo.SetValue(this, byteTransform.TransUInt16(Content, index));
                        index += sizeof(ushort);
                        break;
                    case TypeCode.Single:
                        propertyInfo.SetValue(this, byteTransform.TransSingle(Content, index));
                        index += sizeof(float);
                        break;
                    case TypeCode.Int32:
                        propertyInfo.SetValue(this, byteTransform.TransInt32(Content, index));
                        index += sizeof(int);
                        break;
                    case TypeCode.UInt32:
                        propertyInfo.SetValue(this, byteTransform.TransUInt32(Content, index));
                        index += sizeof(uint);
                        break;
                    case TypeCode.String:
                        ushort dataLength = CheckStringAttribute(propertyInfo);
                        propertyInfo.SetValue(this, Encoding.Default.GetString(Content, index + 2, Content[index + 1]));
                        index += dataLength;
                        break;
                    default:
                        throw new NotSupportedException("Unsupported primitive type: " + propertyInfo.PropertyType);
                }
            }
        }

        public byte[] ToSource()
        {
            int propertyValueHasNull = -1;
            List<byte> bytes = new List<byte>();
            PropertyInfo[] propertyInfos = GetType().GetProperties().Where(x => x.CanWrite).ToArray();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                object? obj = propertyInfo.GetValue(this);
                if (obj == null && propertyValueHasNull != -1)
                {
                    throw new Exception($"{nameof(DeviceCommand)}错误");
                }
                else
                {
                    if (propertyValueHasNull == -1)
                        propertyValueHasNull = 2;
                }
                TypeCode typeCode = Type.GetTypeCode(propertyInfo.PropertyType);
                switch (typeCode)
                {
                    case TypeCode.Byte:
                    case TypeCode.SByte:
                    case TypeCode.Char:
                        if (obj != null)
                        {
                            bytes.Add((byte)obj);
                        }
                        else
                        { propertyValueHasNull = 1; }
                        break;
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                        if (obj != null)
                        {
                            byte[] bytesShort = BitConverter.GetBytes(Convert.ToUInt16(obj));
                            Array.Reverse(bytesShort);
                            bytes.AddRange(bytesShort);
                        }
                        else
                        { propertyValueHasNull = 1; }
                        break;
                    case TypeCode.Single:
                        if (obj != null)
                        {
                            byte[] bytesSingle = BitConverter.GetBytes(Convert.ToSingle(obj));
                            Array.Reverse(bytesSingle);
                            bytes.AddRange(bytesSingle);
                        }
                        else
                        { propertyValueHasNull = 1; }
                        break;
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                        if (obj != null)
                        {
                            byte[] bytesInt = BitConverter.GetBytes(Convert.ToUInt32(obj));
                            Array.Reverse(bytesInt);
                            bytes.AddRange(bytesInt);
                        }
                        else
                        { propertyValueHasNull = 1; }
                        break;
                    case TypeCode.String:

                        if (obj != null)
                        {
                            ushort dataLength = CheckStringAttribute(propertyInfo);
                            byte[] bytesString = Encoding.Default.GetBytes(obj.ToString());
                            bytes.Add(Convert.ToByte(dataLength));
                            bytes.Add(Convert.ToByte(obj.ToString().Length));
                            bytes.AddRange(bytesString);
                            for (int i = 0; i < dataLength - obj.ToString().Length - 2; i++)
                            {
                                bytes.Add(0);
                            }
                        }
                        else
                        { propertyValueHasNull = 1; }
                        break;
                    default:
                        throw new NotSupportedException("Unsupported primitive type: " + propertyInfo.PropertyType);
                }
            }

            return bytes.ToArray();
        }
        #endregion <Public Method>

        #region <Event>
        #endregion <Event>
    }
}