using JiShe.CollectBus.Common.Enums;
using JiShe.CollectBus.Common.Extensions;
using JiShe.CollectBus.Common.Helpers;
using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
using NUglify.JavaScript.Syntax;
using System.Text.RegularExpressions;

namespace JiShe.CollectBus.Protocol.T37612012
{
    public static class Protocol3761Extensions
    {

         /// <summary>
        /// 3761协议数据字节校验
         /// </summary>
         /// <param name="value"></param>
         /// <returns></returns>
        public static bool Check3761Byte(this string value)
        {
            if (!value.ToUpper().Equals("FF") && !value.ToUpper().Equals("EE"))
                return true;
            return false;
        }

        /// <summary>
        /// 字符串中是否包含字母
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsLetterExists(this string value)
        {
            return Regex.Matches(value, "[a-zA-Z]").Count > 0;
        }

        /// <summary>
        /// 判断错误代码 
        /// </summary>
        public static Tuple<string,string>? CheckErrorCode(this List<string> data)
        {
            var value = string.Join("", data);
            if (value.IsLetterExists())
            {
                var code = ErrorCodes().Where(f =>  value.Contains(f.Key)).FirstOrDefault();
                if (code.Key != null)
                    return Tuple.Create(code.Key,code.Value);
                else
                  return Tuple.Create("EE", "未知数据错误"); 
            }
            return null;
        }

        /// <summary>
        /// 判断错误代码 
        /// </summary>
        public static Tuple<string, string>? CheckErrorCode(this string value)
        {
            if (value.IsLetterExists())
            {
                var code = ErrorCodes().Where(f => value.Contains(f.Key)).FirstOrDefault();
                if (code.Key != null)
                    return Tuple.Create(code.Key, code.Value);
                else
                    return Tuple.Create("EE", "未知数据错误");
            }
            return null;
        }

        /// <summary>
        /// 错误信息
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> ErrorCodes()
        {
            return new Dictionary<string, string>() 
            {
                { "FF", "电表无此数据项" },
                { "EE", "未知数据错误" },
                { "E1", "数据点缺少(停电)" },
                { "E2", "通讯异常" },
                { "E3", "集中器未配置数据项" },
                { "E4", "电表档案无效" },
                { "E5", "电表无此数据项" },
                { "E6", "电表时间异常" },
                { "E7","暂停抄表" }
            };
        }


        /// <summary>
        /// 费率数
        /// </summary>
        /// <param name="index"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static int GetRatingCount(this List<string> hexMessageList ,int index, int len)
        {
            var list = hexMessageList.GetRange(index, len);
            return list.Count > 0 ? list[0].HexToDec() : 0;
        }


        /// <summary>
        /// 抄表时间
        /// </summary>
        /// <param name="index"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static string GetReadTime(this List<string> hexMessageList, int index, int len)
        {
            var list = hexMessageList.GetRange(index, len);
            return list.GetReadTime();
        }

        /// <summary>
        /// 抄表时间
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string GetReadTime(this List<string> data)
        {
            data.Reverse();
            data.Insert(0, DateTime.Now.ToString("yyyy").Substring(0, 2));
            return string.Join("", data);
        }


        /// <summary>
        /// 判断当前时间所在时标(15分钟)
        /// </summary>
        /// <param name="curTime"></param>
        /// <returns></returns>
        public static DateTime CheckTimePoint(this DateTime curTime)
        {
            var curMinute = Convert.ToInt32(curTime.ToString("mm"));
            string dataTime;
            if (curMinute >= 0 && curMinute < 15)
                dataTime = curTime.ToString("yyyy-MM-dd HH:00:00");
            else if (curMinute >= 15 && curMinute < 30)
                dataTime = curTime.ToString("yyyy-MM-dd HH:15:00");
            else if (curMinute >= 30 && curMinute < 45)
                dataTime = curTime.ToString("yyyy-MM-dd HH:30:00");
            else
                dataTime = curTime.ToString("yyyy-MM-dd HH:45:00");
            return DateTime.Parse(dataTime);
        }

        /// <summary>
        /// 判断当前时间所在时标(5分钟)
        /// </summary>
        /// <param name="curTime"></param>
        /// <returns></returns>
        public static DateTime CheckTimeFivePoint(this DateTime curTime)
        {
            string dataTime;
            var curMinute = Convert.ToInt32(curTime.ToString("mm"));
            if (curMinute >= 0 && curMinute < 5)
                dataTime = curTime.ToString("yyyy-MM-dd HH:00:00");
            else if (curMinute >= 5 && curMinute < 10)
                dataTime = curTime.ToString("yyyy-MM-dd HH:5:00");
            else if (curMinute >= 10 && curMinute < 15)
                dataTime = curTime.ToString("yyyy-MM-dd HH:10:00");
            else if (curMinute >= 15 && curMinute < 20)
                dataTime = curTime.ToString("yyyy-MM-dd HH:15:00");
            else if (curMinute >= 20 && curMinute < 25)
                dataTime = curTime.ToString("yyyy-MM-dd HH:20:00");
            else if (curMinute >= 25 && curMinute < 30)
                dataTime = curTime.ToString("yyyy-MM-dd HH:25:00");
            else if (curMinute >= 30 && curMinute < 35)
                dataTime = curTime.ToString("yyyy-MM-dd HH:30:00");
            else if (curMinute >= 35 && curMinute < 40)
                dataTime = curTime.ToString("yyyy-MM-dd HH:35:00");
            else if (curMinute >= 40 && curMinute < 45)
                dataTime = curTime.ToString("yyyy-MM-dd HH:40:00");
            else if (curMinute >= 45 && curMinute < 50)
                dataTime = curTime.ToString("yyyy-MM-dd HH:45:00");
            else if (curMinute >= 50 && curMinute < 55)
                dataTime = curTime.ToString("yyyy-MM-dd HH:50:00");
            else
                dataTime = curTime.ToString("yyyy-MM-dd HH:55:00");
            return DateTime.Parse(dataTime);
        }

        /// <summary>
        /// 数据时标
        /// </summary>
        /// <param name="hexMessageList"></param>
        /// <param name="index"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static List<string> GetReadTimeTd_c(this List<string> hexMessageList, int index, int len)
        {
            if (len != 7)
                throw new ArgumentException("数据时标 Td_c参数为不标准长度");
            var list = hexMessageList.GetRange(index, len);
            List<string> values = new List<string>
            {
                GetReadTime(list.GetRange(0, 5)),
                list[5].HexToDec().ToString(),//密度
                list[6].HexToDec().ToString()//数据点数
            };
            return values;
        }



        /// <summary>
        /// 数据时标型(曲线类型)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static List<AnalysisBaseDto<decimal>> GenerateFinalResultTd_c(this List<string> data, int index,int density,string dataType, string filedDesc = "")
        {
            List<AnalysisBaseDto<decimal>> list = new List<AnalysisBaseDto<decimal>>();
            for (int i = index; i < data.Count; i++)
            {
                AnalysisBaseDto<decimal> meter = new AnalysisBaseDto<decimal>();
                meter.MeterType= MeterTypeEnum.Ammeter;

                decimal value = 0;
                var errorCode = data[i].CheckErrorCode();
                if (errorCode != null)
                {
                    meter.ValidData = false;
                    meter.ErrorCodeMsg = errorCode.Item2;
                }
                else
                    decimal.TryParse(data[i], out value);
                meter.DataValue = value;
                meter.DataType = dataType;
                meter.FiledDesc = filedDesc;
                meter.FiledName = meter.DataType.GetDataFieldByGatherDataType() ?? string.Empty;
                if (DateTime.TryParse(CalculateTimeSpan(i - 3, data[0], density), out DateTime readingDate))
                    meter.TimeSpan = readingDate;
                list.Add(meter);
            }
            return list;
        }

        /// <summary>
        /// 月冻结
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <param name="dataType"></param>
        /// <param name="timeSpan"></param>
        /// <param name="remark"></param>
        /// <returns></returns>
        public static List<AnalysisBaseDto<decimal>> GenerateFinalResultTd_m(this List<string> data, int index,string dataType,string timeSpan, string filedDesc = "")
        {
            List<AnalysisBaseDto<decimal>> list = new List<AnalysisBaseDto<decimal>>();
            for (int i = index; i < data.Count; i++)
            {
                AnalysisBaseDto<decimal> meter = new AnalysisBaseDto<decimal>();
                meter.MeterType = MeterTypeEnum.Ammeter;
                decimal value = 0;
                var errorCode = data[i].CheckErrorCode();
                if (errorCode != null)
                {
                    meter.ValidData = false;
                    meter.ErrorCodeMsg = errorCode.Item2;
                }
                else
                    decimal.TryParse(data[i], out value);
                meter.DataValue = value;
                if (DateTime.TryParse(timeSpan, out DateTime readingDate))
                {
                    meter.TimeSpan = readingDate;
                }
                meter.DataType = i - index == 0 ? dataType : $"{dataType}_{i - index}";
                meter.FiledDesc = filedDesc;
                meter.FiledName = meter.DataType.GetDataFieldByGatherDataType() ?? string.Empty;
                list.Add(meter);
            }
            return list;
        }

        /// <summary>
        /// 日冻结
        /// </summary>
        /// <param name="index"></param>
        /// <param name="data"></param>
        /// <param name="remark"></param>
        /// <returns></returns>
        public static List<AnalysisBaseDto<decimal>> GenerateFinalResultTd_d(this List<string> data, int index,string dataType, string timeSpan,  string filedDesc = "")
        {
            List<AnalysisBaseDto<decimal>> list = new List<AnalysisBaseDto<decimal>>();
            int typeIndex = 0;
            for (int i = index; i < data.Count; i++)
            {
                AnalysisBaseDto<decimal> meter = new AnalysisBaseDto<decimal>
                {
                    MeterType = MeterTypeEnum.Ammeter
                };
                decimal value = 0;
                var errorCode = data[i].CheckErrorCode();
                if (errorCode != null)
                {
                    meter.ValidData = false;
                    meter.ErrorCodeMsg = errorCode.Item2;
                }
                else
                    decimal.TryParse(data[i], out value);
                meter.DataValue = value;
                if (DateTime.TryParse(timeSpan, out DateTime readingDate))
                {
                    meter.TimeSpan = readingDate;
                }
                meter.DataType = i - index == 0 ? dataType : $"{dataType}_{i - index}";
                meter.FiledDesc = filedDesc;
                meter.FiledName = meter.DataType.GetDataFieldByGatherDataType() ?? string.Empty;
                list.Add(meter);
                typeIndex++;
            }
            return list;
        }

        /// <summary>
        /// 计算时标
        /// </summary>
        /// <param name="index"></param>
        /// <param name="time"></param>
        /// <param name="intervalTime"></param>
        /// <returns></returns>
        private static string CalculateTimeSpan(int index, string time, int intervalTime)
        {
            var startTime = Convert.ToDateTime($"{time.Substring(0, 4)}-{time.Substring(4, 2)}-{time.Substring(6, 2)} {time.Substring(8, 2)}:{time.Substring(10, 2)}");
            return startTime.AddMinutes(index * intervalTime).ToString("yyyy-MM-dd HH:mm:ss");
        }

        /// <summary>
        ///  校验数据值精度
        /// </summary>
        /// <param name="meterDatas"></param>
        /// <param name="mark"></param>
        /// <returns></returns>
        public static List<AnalysisBaseDto<decimal>> IsValidData(this List<AnalysisBaseDto<decimal>> meterDatas, List<string> mark)
        {
            bool isUpload = false;
            var jfpgSum = 0M;
            var totalItem = meterDatas.Find(f => f.DataType.Equals(mark[0]));//meterDatas.Find(f => f.DataType.Equals(mark[0]));
            for (int i = 1; i <= meterDatas.Count - 1; i++)
            {
                var value = meterDatas[i];//meterDatas.Find(f => f.DataType.Equals(mark[i]));
                if (value.ValidData)
                    jfpgSum += value.DataValue;
            }
            var floatingNum = (jfpgSum * 5 / 100);//上下浮动数据
            var minjfpgSum = jfpgSum - floatingNum;//100-(100*5/100);
            var maxjfpgSum = jfpgSum + floatingNum;
            if (totalItem.DataValue <= maxjfpgSum || totalItem.DataValue >= minjfpgSum)//总值，在JFPG之和的浮动范围内
                isUpload = true;
            else
                isUpload = false;
            if (!isUpload)
                meterDatas.ForEach(f =>
                {
                    f.ValidData = false;
                });
            return meterDatas;
        }
    }
}
