using HslCommunication.Core;
using System;

namespace HslCommunication.BasicFramework
{
    /// <summary>
    /// 一个线程安全的缓存数据块,支持批量动态修改,添加,并获取快照
    /// </summary>
    /// <remarks>
    /// 这个类可以实现什么功能呢,就是你有一个大的数组,作为你的应用程序的中间数据池,允许你往byte[]数组里存放指定长度的子byte[]数组,也允许从里面拿数据,
    /// 这些操作都是线程安全的,当然,本类扩展了一些额外的方法支持,也可以直接赋值或获取基本的数据类型对象。
    /// </remarks>
    /// <example>
    /// 此处举例一些数据的读写说明,可以此处的数据示例。
    /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\BasicFramework\SoftBufferExample.cs" region="SoftBufferExample1" title="SoftBuffer示例" />
    /// </example>
    public class SoftBuffer : IDisposable
    {
        #region Constructor

        /// <summary>
        /// 使用默认的大小初始化缓存空间
        /// </summary>
        public SoftBuffer()
        {
            buffer = new byte[capacity];
            hybirdLock = new SimpleHybirdLock();
            byteTransform = new RegularByteTransform();
        }

        /// <summary>
        /// 使用指定的容量初始化缓存数据块
        /// </summary>
        /// <param name="capacity">初始化的容量</param>
        public SoftBuffer(int capacity)
        {
            buffer = new byte[capacity];
            this.capacity = capacity;
            hybirdLock = new SimpleHybirdLock();
            byteTransform = new RegularByteTransform();
        }

        #endregion

        #region Bool Operate Support

        /// <summary>
        /// 设置指定的位置的数据块,如果超出,则丢弃数据
        /// </summary>
        /// <param name="value">bool值</param>
        /// <param name="destIndex">目标存储的索引</param>
        /// <exception cref="IndexOutOfRangeException"></exception>
        public void SetBool(bool value, int destIndex)
        {
            SetBool(new bool[] { value }, destIndex);
        }

        /// <summary>
        /// 设置指定的位置的数据块,如果超出,则丢弃数据
        /// </summary>
        /// <param name="value">bool数组值</param>
        /// <param name="destIndex">目标存储的索引</param>
        /// <exception cref="IndexOutOfRangeException"></exception>
        public void SetBool(bool[] value, int destIndex)
        {
            if (value != null)
            {
                try
                {
                    hybirdLock.Enter();
                    for (int i = 0; i < value.Length; i++)
                    {
                        int byteIndex = (destIndex + i) / 8;
                        int offect = (destIndex + i) % 8;

                        if (value[i])
                        {
                            buffer[byteIndex] = (byte)(buffer[byteIndex] | getOrByte(offect));
                        }
                        else
                        {
                            buffer[byteIndex] = (byte)(buffer[byteIndex] & getAndByte(offect));
                        }
                    }

                    hybirdLock.Leave();
                }
                catch
                {
                    hybirdLock.Leave();
                    throw;
                }
            }

        }

        /// <summary>
        /// 获取指定的位置的bool值,如果超出,则引发异常
        /// </summary>
        /// <param name="destIndex">目标存储的索引</param>
        /// <returns>获取索引位置的bool数据值</returns>
        /// <exception cref="IndexOutOfRangeException"></exception>
        public bool GetBool(int destIndex)
        {
            return GetBool(destIndex, 1)[0];
        }

        /// <summary>
        /// 获取指定位置的bool数组值,如果超过,则引发异常
        /// </summary>
        /// <param name="destIndex">目标存储的索引</param>
        /// <param name="length">读取的数组长度</param>
        /// <exception cref="IndexOutOfRangeException"></exception>
        /// <returns>bool数组值</returns>
        public bool[] GetBool(int destIndex, int length)
        {
            bool[] result = new bool[length];
            try
            {
                hybirdLock.Enter();
                for (int i = 0; i < length; i++)
                {
                    int byteIndex = (destIndex + i) / 8;
                    int offect = (destIndex + i) % 8;

                    result[i] = (buffer[byteIndex] & getOrByte(offect)) == getOrByte(offect);
                }

                hybirdLock.Leave();
            }
            catch
            {
                hybirdLock.Leave();
                throw;
            }
            return result;
        }

        private byte getAndByte(int offect)
        {
            switch (offect)
            {
                case 0: return 0xFE;
                case 1: return 0xFD;
                case 2: return 0xFB;
                case 3: return 0xF7;
                case 4: return 0xEF;
                case 5: return 0xDF;
                case 6: return 0xBF;
                case 7: return 0x7F;
                default: return 0xFF;
            }
        }


        private byte getOrByte(int offect)
        {
            switch (offect)
            {
                case 0: return 0x01;
                case 1: return 0x02;
                case 2: return 0x04;
                case 3: return 0x08;
                case 4: return 0x10;
                case 5: return 0x20;
                case 6: return 0x40;
                case 7: return 0x80;
                default: return 0x00;
            }
        }


        #endregion

        #region Byte Operate Support

        /// <summary>
        /// 设置指定的位置的数据块,如果超出,则丢弃数据
        /// </summary>
        /// <param name="data">数据块信息</param>
        /// <param name="destIndex">目标存储的索引</param>
        public void SetBytes(byte[] data, int destIndex)
        {
            if (destIndex < capacity && destIndex >= 0 && data != null)
            {
                hybirdLock.Enter();

                if ((data.Length + destIndex) > buffer.Length)
                {
                    Array.Copy(data, 0, buffer, destIndex, (buffer.Length - destIndex));
                }
                else
                {
                    data.CopyTo(buffer, destIndex);
                }

                hybirdLock.Leave();
            }
        }

        /// <summary>
        /// 设置指定的位置的数据块,如果超出,则丢弃数据
        /// </summary>
        /// <param name="data">数据块信息</param>
        /// <param name="destIndex">目标存储的索引</param>
        /// <param name="length">准备拷贝的数据长度</param>
        public void SetBytes(byte[] data, int destIndex, int length)
        {
            if (destIndex < capacity && destIndex >= 0 && data != null)
            {
                if (length > data.Length) length = data.Length;

                hybirdLock.Enter();

                if ((length + destIndex) > buffer.Length)
                {
                    Array.Copy(data, 0, buffer, destIndex, (buffer.Length - destIndex));
                }
                else
                {
                    Array.Copy(data, 0, buffer, destIndex, length);
                }

                hybirdLock.Leave();
            }
        }

        /// <summary>
        /// 设置指定的位置的数据块,如果超出,则丢弃数据
        /// </summary>
        /// <param name="data">数据块信息</param>
        /// <param name="sourceIndex">Data中的起始位置</param>
        /// <param name="destIndex">目标存储的索引</param>
        /// <param name="length">准备拷贝的数据长度</param>
        /// <exception cref="IndexOutOfRangeException"></exception>
        public void SetBytes(byte[] data, int sourceIndex, int destIndex, int length)
        {
            if (destIndex < capacity && destIndex >= 0 && data != null)
            {
                if (length > data.Length) length = data.Length;

                hybirdLock.Enter();

                Array.Copy(data, sourceIndex, buffer, destIndex, length);

                hybirdLock.Leave();
            }
        }

        /// <summary>
        /// 获取内存指定长度的数据信息
        /// </summary>
        /// <param name="index">起始位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>返回实际的数据信息</returns>
        public byte[] GetBytes(int index, int length)
        {
            byte[] result = new byte[length];
            if (length > 0)
            {
                hybirdLock.Enter();
                if (index >= 0 && (index + length) <= buffer.Length)
                {
                    Array.Copy(buffer, index, result, 0, length);
                }
                hybirdLock.Leave();
            }
            return result;
        }

        /// <summary>
        /// 获取内存所有的数据信息
        /// </summary>
        /// <returns>实际的数据信息</returns>
        public byte[] GetBytes()
        {
            return GetBytes(0, capacity);
        }

        #endregion

        #region BCL Set Support

        /// <summary>
        /// 设置byte类型的数据到缓存区
        /// </summary>
        /// <param name="value">byte数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(byte value, int index)
        {
            SetBytes(new byte[] { value }, index);
        }

        /// <summary>
        /// 设置short类型的数据到缓存区
        /// </summary>
        /// <param name="values">short数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(short[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置short类型的数据到缓存区
        /// </summary>
        /// <param name="value">short数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(short value, int index)
        {
            SetValue(new short[] { value }, index);
        }

        /// <summary>
        /// 设置ushort类型的数据到缓存区
        /// </summary>
        /// <param name="values">ushort数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(ushort[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置ushort类型的数据到缓存区
        /// </summary>
        /// <param name="value">ushort数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(ushort value, int index)
        {
            SetValue(new ushort[] { value }, index);
        }

        /// <summary>
        /// 设置int类型的数据到缓存区
        /// </summary>
        /// <param name="values">int数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(int[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置int类型的数据到缓存区
        /// </summary>
        /// <param name="value">int数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(int value, int index)
        {
            SetValue(new int[] { value }, index);
        }

        /// <summary>
        /// 设置uint类型的数据到缓存区
        /// </summary>
        /// <param name="values">uint数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(uint[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置uint类型的数据到缓存区
        /// </summary>
        /// <param name="value">uint数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(uint value, int index)
        {
            SetValue(new uint[] { value }, index);
        }

        /// <summary>
        /// 设置float类型的数据到缓存区
        /// </summary>
        /// <param name="values">float数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(float[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置float类型的数据到缓存区
        /// </summary>
        /// <param name="value">float数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(float value, int index)
        {
            SetValue(new float[] { value }, index);
        }

        /// <summary>
        /// 设置long类型的数据到缓存区
        /// </summary>
        /// <param name="values">long数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(long[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置long类型的数据到缓存区
        /// </summary>
        /// <param name="value">long数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(long value, int index)
        {
            SetValue(new long[] { value }, index);
        }

        /// <summary>
        /// 设置ulong类型的数据到缓存区
        /// </summary>
        /// <param name="values">ulong数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(ulong[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置ulong类型的数据到缓存区
        /// </summary>
        /// <param name="value">ulong数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(ulong value, int index)
        {
            SetValue(new ulong[] { value }, index);
        }

        /// <summary>
        /// 设置double类型的数据到缓存区
        /// </summary>
        /// <param name="values">double数组</param>
        /// <param name="index">索引位置</param>
        public void SetValue(double[] values, int index)
        {
            SetBytes(byteTransform.TransByte(values), index);
        }

        /// <summary>
        /// 设置double类型的数据到缓存区
        /// </summary>
        /// <param name="value">double数值</param>
        /// <param name="index">索引位置</param>
        public void SetValue(double value, int index)
        {
            SetValue(new double[] { value }, index);
        }

        #endregion

        #region BCL Get Support

        /// <summary>
        /// 获取byte类型的数据
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>byte数值</returns>
        public byte GetByte(int index)
        {
            return GetBytes(index, 1)[0];
        }

        /// <summary>
        /// 获取short类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>short数组</returns>
        public short[] GetInt16(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 2);
            return byteTransform.TransInt16(tmp, 0, length);
        }

        /// <summary>
        /// 获取short类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>short数据</returns>
        public short GetInt16(int index)
        {
            return GetInt16(index, 1)[0];
        }

        /// <summary>
        /// 获取ushort类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>ushort数组</returns>
        public ushort[] GetUInt16(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 2);
            return byteTransform.TransUInt16(tmp, 0, length);
        }

        /// <summary>
        /// 获取ushort类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>ushort数据</returns>
        public ushort GetUInt16(int index)
        {
            return GetUInt16(index, 1)[0];
        }

        /// <summary>
        /// 获取int类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>int数组</returns>
        public int[] GetInt32(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 4);
            return byteTransform.TransInt32(tmp, 0, length);
        }

        /// <summary>
        /// 获取int类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>int数据</returns>
        public int GetInt32(int index)
        {
            return GetInt32(index, 1)[0];
        }

        /// <summary>
        /// 获取uint类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>uint数组</returns>
        public uint[] GetUInt32(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 4);
            return byteTransform.TransUInt32(tmp, 0, length);
        }

        /// <summary>
        /// 获取uint类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>uint数据</returns>
        public uint GetUInt32(int index)
        {
            return GetUInt32(index, 1)[0];
        }

        /// <summary>
        /// 获取float类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>float数组</returns>
        public float[] GetSingle(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 4);
            return byteTransform.TransSingle(tmp, 0, length);
        }

        /// <summary>
        /// 获取float类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>float数据</returns>
        public float GetSingle(int index)
        {
            return GetSingle(index, 1)[0];
        }

        /// <summary>
        /// 获取long类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>long数组</returns>
        public long[] GetInt64(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 8);
            return byteTransform.TransInt64(tmp, 0, length);
        }

        /// <summary>
        /// 获取long类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>long数据</returns>
        public long GetInt64(int index)
        {
            return GetInt64(index, 1)[0];
        }

        /// <summary>
        /// 获取ulong类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>ulong数组</returns>
        public ulong[] GetUInt64(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 8);
            return byteTransform.TransUInt64(tmp, 0, length);
        }

        /// <summary>
        /// 获取ulong类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>ulong数据</returns>
        public ulong GetUInt64(int index)
        {
            return GetUInt64(index, 1)[0];
        }

        /// <summary>
        /// 获取double类型的数组到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <param name="length">数组长度</param>
        /// <returns>ulong数组</returns>
        public double[] GetDouble(int index, int length)
        {
            byte[] tmp = GetBytes(index, length * 8);
            return byteTransform.TransDouble(tmp, 0, length);
        }

        /// <summary>
        /// 获取double类型的数据到缓存区
        /// </summary>
        /// <param name="index">索引位置</param>
        /// <returns>double数据</returns>
        public double GetDouble(int index)
        {
            return GetDouble(index, 1)[0];
        }

        #endregion

        #region Customer Support

        /// <summary>
        /// 读取自定义类型的数据,需要规定解析规则
        /// </summary>
        /// <typeparam name="T">类型名称</typeparam>
        /// <param name="index">起始索引</param>
        /// <returns>自定义的数据类型</returns>
        public T GetCustomer<T>(int index) where T : IDataTransfer, new()
        {
            T Content = new T();
            byte[] read = GetBytes(index, Content.ReadCount);
            Content.ParseSource(read);
            return Content;
        }

        /// <summary>
        /// 写入自定义类型的数据到缓存中去,需要规定生成字节的方法
        /// </summary>
        /// <typeparam name="T">自定义类型</typeparam>
        /// <param name="data">实例对象</param>
        /// <param name="index">起始地址</param>
        public void SetCustomer<T>(T data, int index) where T : IDataTransfer, new()
        {
            SetBytes(data.ToSource(), index);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// 获取或设置当前的数据缓存类的解析规则
        /// </summary>
        public IByteTransform ByteTransform
        {
            get => byteTransform;
            set => byteTransform = value;
        }

        #endregion

        #region Private Member

        private int capacity = 10;                      // 缓存的容量
        private byte[] buffer;                          // 缓存的数据
        private SimpleHybirdLock hybirdLock;            // 高效的混合锁
        private IByteTransform byteTransform;           // 数据转换类

        #endregion

        #region IDisposable Support

        private bool disposedValue = false; // 要检测冗余调用

        /// <summary>
        /// 释放当前的对象
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    hybirdLock?.Dispose();
                    buffer = null;
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~SoftBuffer()
        // {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。

        /// <summary>
        /// 释放当前的对象
        /// </summary>
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion

    }
}