using FreeRedis;
using Newtonsoft.Json;
using ServiceCenter.Redis;

namespace ServiceCenter.Virtual_PLC
{
    /// <summary>
    /// plc数据
    /// </summary>
    public class PlcData
    {
        private static string RedisKey = "Virtual_PLC";
        private static RedisClient Redis;

        /// <summary>
        /// 数据结构缓存
        /// </summary>
        private static List<PLCData> PLCDatas { get; set; } = new List<PLCData>();

        /// <summary>
        /// redis 链接字符串
        /// </summary>
        /// <param name="redisClient">Redis链接字符串</param>
        public PlcData(string redisClient)
        {
            Redis = RedisHub.CreateContext(redisClient, RedisKey);
        }

        /// <summary>
        /// redis 链接字符串
        /// </summary>
        /// <param name="redisClient"></param>
        /// <returns></returns>
        public static PlcData Init(string redisClient)
        {
            return new PlcData(redisClient);
        }

        /// <summary>
        /// 初始化PLC数据
        /// </summary>
        /// <param name="pLCData">一个PLC</param>
        private void InitPlcData(PLCData pLCData)
        {
            if (!PLCDatas.Contains(pLCData))
            {
                PLCDatas.Add(pLCData);
            }
            //用总长度除以数据长度，再以每断数据的起始位置、IP、DB组成Key
            var mun = pLCData.Length / pLCData.DataLength;
            int addstart = 0;

            for (int i = 0; i < mun; i++)
            {
                var key = $"{pLCData.IP}:{pLCData.DB}:{addstart}";
                if (Redis.Exists(key)) continue;
                Redis.Set(key, new byte[pLCData.DataLength]);
                addstart = addstart + pLCData.DataLength;
            }
        }

        /// <summary>
        /// 初始化PLC数据
        /// </summary>
        /// <param name="pLCDatas">多个PLC</param>
        public void InitPlcData(List<PLCData> pLCDatas)
        {
            string key = "PLCDataList";
            if (Redis.Exists(key))
            {
                //判断值是否相等,值相等代表已经有过数据生成了，并且没有任何变化
                var RedisPlcData = Redis.Get(key);
                if (JsonConvert.SerializeObject(pLCDatas) == RedisPlcData)
                {
                    PLCDatas = pLCDatas;
                    return;
                }
            }

            pLCDatas.ForEach(v =>
            {
                InitPlcData(v);
            });
            Redis.Set(key, JsonConvert.SerializeObject(pLCDatas));
        }

        /// <summary>
        /// 按照DB读取
        /// </summary>
        /// <param name="pLCData"></param>
        /// <returns></returns>
        public static byte[] Read(PLCData pLCData)
        {
            byte[] data = new byte[pLCData.Length];
            //用总长度除以数据长度，再以每断数据的起始位置、IP、DB组成Key
            var mun = pLCData.Length / pLCData.DataLength;
            int addstart = 0;

            for (int i = 0; i < mun; i++)
            {
                var a = Redis.Get<byte[]>($"{pLCData.IP}:{pLCData.DB}:{addstart}");
                a.CopyTo(data, addstart);
                addstart = addstart + pLCData.DataLength;
            }
            return data;
        }

        /// <summary>
        /// 按照长度读取
        /// </summary>
        /// <param name="pLCData"></param>
        /// <param name="startLength">起始长度</param>
        /// <returns></returns>
        public static byte[] Read(PLCData pLCData, int startLength)
        {
            return System.Text.Encoding.Default.GetBytes(Redis.Get($"{pLCData.IP}:{pLCData.DB}:{startLength}"));
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="pLCData"></param>
        /// <param name="startLength"></param>
        /// <param name="value"></param>
        public static void Write(PLCData pLCData, int startLength, byte[] value)
        {
            var data = PLCDatas.Find(v => v.IP == pLCData.IP && v.DB == pLCData.DB);
            int start = 0;
            if (data.DataLength == data.Length) //数据长度与总长度相等时计算
            {
                start = startLength < data.DataLength ? 0 : data.DataLength - startLength + startLength;
            }
            else //不等
            {
                int addstart = 0;
                var mun = data.Length / data.DataLength;
                var a = new List<int>();
                for (int i = 0; i < mun; i++)
                {
                    a.Add(addstart);
                    addstart = addstart + data.DataLength;
                }
                start = a.Where(v => v <= startLength).OrderByDescending(v => v).FirstOrDefault();
                startLength = startLength - start;
            }
            var bytes = System.Text.Encoding.Default.GetBytes(Redis.Get($"{pLCData.IP}:{pLCData.DB}:{start}")); //获取原有数据
            value.CopyTo(bytes, startLength); //将变更的数据，更新到redis字节组中
            Redis.Set($"{pLCData.IP}:{pLCData.DB}:{start}", bytes);
        }
    }

    /// <summary>
    /// PLC数据结构
    /// </summary>
    public class PLCData
    {
        /// <summary>
        /// IP
        /// </summary>
        public string IP { get; set; }

        /// <summary>
        /// DB
        /// </summary>
        public int DB { get; set; }

        /// <summary>
        /// 总长度
        /// </summary>
        public int Length { get; set; }

        /// <summary>
        /// 数据长度
        /// </summary>
        public int DataLength { get; set; }
    }
}
