using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.ComponentModel;
using System.Xml.Serialization;

using Misc;
using FObjBase;
using FlyADBase;
using FLY.Thick.Base.IService;
using PropertyChanged;
using Newtonsoft.Json;
using static Misc.ReverserInfo;
using FLY.Thick.Base.Common;
using AutoMapper;

namespace FLY.Thick.Base.Server
{
    public class GSample : IGetSampleService
    {
        /// <summary>
        /// 报警测试
        /// </summary>

        #region IGetSampleService 接口

        /// <summary>
        /// 参数:使能
        /// </summary>
        public bool Enable { get; set; } = true;

        /// <summary>
        /// 参数:速度
        /// </summary>
        public UInt32 Velocity { get; set; } = 500;


        /// <summary>
        /// 参数:样品点范围 单位:脉冲
        /// 样品点直径为 Range*2
        /// </summary>
        public int SampleRange { get; set; } = 100;


        /// <summary>
        /// 滤波窗口,单位 min
        /// </summary>
        public int Window { get; set; }

        /// <summary>
        /// 使用%方式检查异常
        /// </summary>
        public bool IsCheckByPercent { get; set; } = true;

        /// <summary>
        /// 异常比例 单位不是%
        /// </summary>
        public double ErrPercent { get; set; } = 0.02;
        
        /// <summary>
        /// 异常值
        /// </summary>
        public int ErrValue { get; set; } = 200;

        /// <summary>
        /// 当前 样品X_AD/样品0_AD 与 上一次比较 差异比例 单位不是%
        /// </summary>
        public double CrossErrPercent { get; set; } = 0.02;

        /// <summary>
        /// 异常比较 时间间隔范围;
        /// 两次时间点比较, 时间点的间隔最大时长。 大于它无效,不能比较
        /// </summary>
        public int ErrIntervalMin { get; set; } = 30;

        /// <summary>
        /// 样品
        /// </summary>
        public SampleCell[] Samples { get; set; }

        /// <summary>
        /// 参数:特征查找范围 单位:脉冲
        /// 取样范围 会扩大为 -SearchRange ~ +SearchRange
        /// </summary>
        public int SearchRange { get; set; } = 100;

        /// <summary>
        /// 特征  相识度 0 正, 1 反
        /// </summary>
        public SampleFeature[] Features { get; set; } 


        #endregion
        
        
        TempFilter2[] tempFilters;
        DIRECTION[] directions;
        IGageInfoService mGageInfo;
        OBJComponents.Server.WarningSystem2 warningSystem;
        FlyAD7 flyad;

        public event SampleChangedEventHandler SampleChangedEvent;
        private string param_path;

        
        public GSample():this("gsample.json")
        {

        }
        public GSample(string param_path)
        {
            this.param_path = param_path;

            Samples = new SampleCell[3];

            Features = new SampleFeature[2];
            tempFilters = new TempFilter2[Samples.Count()];
            directions = new DIRECTION[Samples.Count()];

            for (int i = 0; i < Samples.Count(); i++)
                tempFilters[i] = new TempFilter2();
            
            Enable = true;
            SampleRange = 100;//样品范围
            Velocity = 200;//取样速度
            Window = 3;//移动窗口
            IsCheckByPercent = true;
            ErrPercent = 2;
            ErrValue = 200;

            Samples[0] = new SampleCell()
            {
                Enable = true,
                Position = 200,
                OrgAD = 50300
            };
            directions[0] = DIRECTION.BACKWARD;

            Samples[1] = new SampleCell()
            {
                Enable = false,
                Position = 0,
                OrgAD = 0
            };
            directions[1] = DIRECTION.FORWARD;

            Samples[2] = new SampleCell()
            {
                Enable = false,
                Position = 0,
                OrgAD = 0
            };
            directions[2] = DIRECTION.FORWARD;

            SearchRange = 100;
            Features[0] = new SampleFeature() { StartPos = 0, EndPos = 0 };
            Features[1] = new SampleFeature() { StartPos = 2000, EndPos = 2100 };

            Load();
        }
        public virtual void Init(

            FlyAD7 flyad,
            IGageInfoService gageInfo,
            OBJComponents.Server.WarningSystem2 warningSystem) 
        {
            this.flyad = flyad;
            this.mGageInfo = gageInfo;
            this.warningSystem = warningSystem;



        }

        public bool Load() 
        {
            return GSampleJsonDb.Load(this, param_path);
        }

        /// <summary>
        /// 保存
        /// </summary>
        public bool Save() 
        {
            return GSampleJsonDb.Save(this, param_path);
        }
        /// <summary>
        /// 1.判断样品取样方向
        /// 2.复位间隔计数
        /// 3.复位所有样品取样状态
        /// </summary>
        /// <param name="scanbegin"></param>
        /// <param name="scanend"></param>
        public virtual void Reset(int scanbegin, int scanend)
        {
            for (int i = 0; i < Samples.Count(); i++) {
                SampleCell sample = Samples[i];

                int pos = sample.Position;
                directions[i] = (Math.Abs(pos - scanbegin) < Math.Abs(pos - scanend)) ? Misc.DIRECTION.BACKWARD : Misc.DIRECTION.FORWARD;
                
                sample.AD = -1;
                tempFilters[i].Reset();
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public void Reset() 
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];

                sample.AD = -1;
                tempFilters[i].Reset();
            }
        }

        /// <summary>
        /// 获取样品测量范围
        /// </summary>
        /// <param name="direction"></param>
        /// <returns>当没有样品,也没有特征,返回null</returns>      
        public Range GetSampleRange(Misc.DIRECTION direction) 
        {
            int b, e;
            if (GetSampleRange(direction, out b, out e))
            {
                return new Range() { Begin = b, End = e };
            }
            else
            {
                return new Range();
            }
        }
        /// <summary>
        /// 获取样品测量范围
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns>当没有样品,也没有特征,返回False</returns>
        public virtual bool GetSampleRange(Misc.DIRECTION direction, out int start,out int end)
        {
            start = 0;
            end = 0;
            bool valid = false;
            int b=0, e=0;
            int r_idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;

            if (Features[r_idx].Enable)
            {
                b = Features[r_idx].StartPos;
                e = Features[r_idx].EndPos;
                valid = true;
            }

            for(int i=0;i< Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                if ((sample.Enable) && (directions[i] == direction))
                {
                    int _b = sample.Position - SampleRange;
                    int _e = sample.Position + SampleRange;

                    if (!valid)
                    {
                        b = _b;
                        e = _e;
                        valid = true;
                    }
                    else
                    {
                        if (b > _b)
                            b = _b;

                        if (e < _e)
                            e = _e;
                    }
                }
            }




            if (valid)
            {
                if (Features[r_idx].Enable)
                {
                    b -= SearchRange;
                    e += SearchRange;
                }
                start = b;
                end = e;
            }

            return valid;
        }


        /// <summary>
        /// 检测参数合法性
        /// </summary>
        /// <returns></returns>
        protected bool CheckParamIsValid() 
        {
            //样品位置 要在 范围
            foreach(SampleCell sample in Samples)
            {
                if (sample.Enable)
                {                    
                    if(((sample.Position -  SampleRange)<0) || ((sample.Position+SampleRange)>= flyad.PosLen))
                        return false;
                }
            }
            foreach(SampleFeature feature in Features)
            {
                if (feature.Enable)
                {                    
                    if(((feature.StartPos -  SearchRange)<0) || ((feature.EndPos+SearchRange)>= flyad.PosLen))
                        return false;
                }
            }
            
            return true;
        }
        
        /// <summary>
        /// 样品取样,推送给thickm, 参数一定合法,不用检测
        /// </summary>
        /// <param name="direction"></param>
        public void Do(Misc.DIRECTION direction)
        {
            int[] dat;
            flyad.GetGrid(direction,0, flyad.PosLen/flyad.PosOfGrid, out dat);
            Do(direction, 0, dat);
        }

        bool CalGridOffset(SampleFeature psr, Misc.DIRECTION direction,int grid_start, int[] buf, out int grid_offset) 
        {
            grid_offset = 0;
            //检测机架数据,当为空时,不能作相识性计算
            if (!mGageInfo.DataOK)
                return true;

            int pos1 = psr.StartPos;
            int pos2 = psr.EndPos;
            int posOfGrid = flyad.PosOfGrid;

            int grid1 = pos1 / posOfGrid;
            int grid2 = pos2 / posOfGrid;
            int grid_searchtol = SearchRange / posOfGrid;

            //获取相同方向的机架信息数据
            int index = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            int[] gagedata = null;
            mGageInfo.GetGageInfo(index, (asyncContext, retData) =>
            {
                var reponse = retData as GetGageInfoResponse;
                gagedata = reponse.Data;
            }, this);


            //合格,查找最大相识性的点
            int grid_b = grid1 - grid_searchtol;
            int grid_e = grid2 + grid_searchtol;
            int grid_len = grid_e - grid_b + 1;
            int[] scanData = new int[grid_len];//这个方向的需要 特征查找范围内的数据,不用考虑 buf 不够 grid_len 

            Array.Copy(buf, grid_b - grid_start, scanData, 0, grid_len);

            double max_relvency = 0;
            int max_grid_offset = 0;

            for (int i = 0; i < grid_searchtol * 2; i++)
            {
                double r = Misc.MyMath.Correl(gagedata, grid1, scanData, i, grid2 - grid1 + 1);
                if (r > max_relvency)
                {
                    max_relvency = r;
                    max_grid_offset = i - grid_searchtol;
                }
            }
            psr.MaxRelevancy = max_relvency;
            psr.MaxOffset = max_grid_offset * posOfGrid;

            if (max_relvency > 0.8) //相识度必须高于0.8
            {
                grid_offset = max_grid_offset;//位置修正
                return true;
            }
            else
            {
                //相似性太低,取样失败!!!
                return false;
            }
        }

        /// <summary>
        /// 设置这个方向 全部样品为失败
        /// </summary>
        /// <param name="direction"></param>
        void SetFailure(Misc.DIRECTION direction) 
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                var tempre = tempFilters[i];
                if ((directions[i] == direction) && (sample.Enable))
                {
                    tempre.Reset();
                    sample.IsFailure = true;
                }
            }
        }

        enum GetSampleAdResult 
        {
            OK,
            /// <summary>
            /// 没有采集数据
            /// </summary>
            Invalid,
            /// <summary>
            /// AD值为0
            /// </summary>
            Zero,
            /// <summary>
            /// 滤波 异常
            /// </summary>
            FilterErr,
            /// <summary>
            /// 与上一个比较,变化太大
            /// </summary>
            TimeCompareErr
        }
        GetSampleAdResult GetSampleAd(SampleCell sample, TempFilter2 tempre, int grid_start, int[] buf, int grid_offset, out int ad) 
        {
            //获取grid 数据
            int posOfGrid = flyad.PosOfGrid;
            int grid = sample.Position / posOfGrid + grid_offset;
            int grid_range = SampleRange / posOfGrid;
            int grid1 = grid - grid_range;
            int grid_len = grid_range * 2;

            int grid_b = grid1 - grid_start;
            int grid_e = grid_b + grid_len - 1;
            //计算平均值
            ad = Misc.MyMath.Avg(buf, grid_b, grid_e);

            if (!Misc.MyBase.ISVALIDATA(ad))//AD无效。。。异常,复位
            {
                ad = -1;

                return GetSampleAdResult.Invalid;
            }
            else
            {
                int orgAd = ad;
                ad = tempre.CalSampleAD(orgAd, TimeSpan.FromMinutes(Window));//ad 滤波
                double diff = Math.Abs(orgAd - ad);
                if (IsCheckByPercent)
                {
                    if (diff == 0)
                    {

                    }
                    else if (ad == 0)
                    {
                        ad = -1;
                        return GetSampleAdResult.Zero;
                    }
                    else if (diff / ad >= ErrPercent) //大于2% 异常
                    {
                        return GetSampleAdResult.FilterErr;
                    }
                }
                else
                {
                    if (diff >= ErrValue)
                    {
                        return GetSampleAdResult.FilterErr;
                    }
                }
                //获取以前的Ad
                int lastAd = tempre.GetLastAd(GetErrIntervalTime);

                if (lastAd != -1) {
                    //找到了
                    diff = Math.Abs(ad - lastAd);
                    if (diff / ad >= ErrPercent) //大于2% 异常
                    {
                        return GetSampleAdResult.TimeCompareErr;
                    }
                }
            }
            return GetSampleAdResult.OK;
        }
        DateTime GetErrIntervalTime => DateTime.Now - TimeSpan.FromMinutes(ErrIntervalMin);

        GetSampleAdResult SetSampleAds(Misc.DIRECTION direction, int grid_start, int[] buf, int grid_offset) 
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                var tempre = tempFilters[i];
                if ((directions[i] == direction) && (sample.Enable))
                {
                    //获取滤波后的AD值
                    var ret = GetSampleAd(sample, tempre, grid_start, buf, grid_offset, out int ad);
                    if(ret == GetSampleAdResult.OK)
                    {
                        sample.AD = ad;
                        sample.IsFailure = false;
                    }
                    else
                    {

                        return ret;
                    }
                }
            }

            if (Samples[0].AD > 0 && Samples[0].IsFailure == false) 
            {
                //交叉比较
                for (int i = 1; i < Samples.Count(); i++)
                {
                    SampleCell sample = Samples[i];
                    if ((directions[i] == direction) && (sample.Enable))
                    {
                        double p = 1.0 * Samples[i].AD / Samples[0].AD;
                        if (p <= 0)
                            break;
                        int lastAdi = tempFilters[i].GetLastAd(GetErrIntervalTime);
                        int lastAd0 = tempFilters[0].GetLastAd(GetErrIntervalTime);
                        if (lastAd0 == 0) 
                            break;
                        
                        double last_p = 1.0 * lastAdi / lastAd0;
                        double diff = Math.Abs(last_p - p);
                        
                        if (diff / p >= CrossErrPercent) //大于2% 异常
                        {
                            //TODO
                            //TODO
                            return GetSampleAdResult.TimeCompareErr;
                        }
                    }
                }
            }
            return GetSampleAdResult.OK;
        }
        /// <summary>
        /// 样品取样,推送给thickm, 参数一定合法,不用检测
        /// </summary>
        /// <param name="direction">方向</param>
        /// <param name="grid_start">开始grid序号</param>
        /// <param name="buf">grid数据</param>
        public virtual void Do(Misc.DIRECTION direction, int grid_start, int[] buf)
        {

            if (CheckParamIsValid() == false)
            {
                this.warningSystem.Update(
                    ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                    $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:参数异常");
                return;
            }
            int grid_offset = 0;//经过相识性计算后的偏移量


            //数据在 gsample -> gagedata[0]
            SampleFeature psr = Features[(direction == Misc.DIRECTION.FORWARD) ? 0 : 1];

            //这个方向的特征查找使能了,位置修正!!!!!!
            if (psr.Enable)
            {
                //通过特征 计算样品偏移, 
                //当没有机架信息,会返回 true
                //成功 计算出偏移 返回 true
                //相关性太低,返回 false
                if (!CalGridOffset(psr, direction, grid_start, buf, out grid_offset))
                {
                    //有问题,全部复位
                    SetFailure(direction);
                    this.warningSystem.Update(
                        ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                        $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:特征查找失败");

                    goto _end;
                }
            }


            //获取这个方向全部样品AD
            var ret = SetSampleAds(direction, grid_start, buf, grid_offset);
            switch (ret) 
            {
                case GetSampleAdResult.Zero:
                    {
                        this.warningSystem.Update(
                            ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                            $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:AD值为0");
                    }
                    break;
                case GetSampleAdResult.Invalid:
                    {
                        this.warningSystem.Update(
                            ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                            $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:没有采集数据");
                    }
                    break;
                case GetSampleAdResult.FilterErr:
                    {
                        this.warningSystem.Update(
                            ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                            $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:滤波异常");
                    }
                    break;
            }

            if(ret != GetSampleAdResult.OK)
            {
                //有问题,全部复位
                SetFailure(direction);
            }
            
_end:
            SampleChangedEvent?.Invoke(this);
        }

        /// <summary>
        /// 检测是否成功
        /// </summary>
        /// <returns></returns>
        public virtual bool OK()
        {
            if (Enable)
            {
                foreach (SampleCell sample in Samples)
                {
                    if (sample.Enable)
                    {
                        if ((sample.AD <= 0))
                            return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 刚才报警了,采样失败
        /// </summary>
        /// <returns></returns>
        public bool IsFailure() 
        {
            if (Enable)
            {
                foreach (SampleCell sample in Samples)
                {
                    if (sample.Enable)
                    {
                        if ((sample.AD <= 0) || (sample.IsFailure))
                            return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 获取N秒的数据,
        /// </summary>
        /// <param name="idx">序号</param>
        /// <param name="buf">数据</param>
        /// <returns></returns>
        public void DoFix(int idx, int[] buf)
        {
            if (idx >= Samples.Count())
                return;
            else if (idx < 0)
                return;

            int avg = Misc.MyMath.Avg(buf);
            SampleCell sample = Samples[idx];
            sample.AD = avg;


            SampleChangedEvent?.Invoke(this);
        }



        public event PropertyChangedEventHandler PropertyChanged;


        public void Apply()
        {
            //通知上层
            Save();
        }

        /// <summary>
        ///  返回 List(List(TempFilterData))
        /// </summary>
        /// <param name="asyncCB"></param>
        /// <param name="asyncContext"></param>
        public void GetTempFilterDatas(AsyncCBHandler asyncDelegate, object asyncContext) 
        {
            List<List<TempFilterData>> ret = new List<List<TempFilterData>>();
            for (int i = 0; i < Samples.Count(); i++) {
                SampleCell sample = Samples[i];
                if (sample.Enable)
                    ret.Add(tempFilters[i].GetDatas());
            }
            asyncDelegate(asyncContext, ret);
        }


    }

    public delegate void SampleChangedEventHandler(GSample gsample);

    public class GSampleJsonDb 
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<GSample, GSampleJsonDb>()
            .ForMember(s => s.Samples, opt =>
            {
                opt.MapFrom(s => Map_SampleCell2SampleCellParam(s.Samples));
            }).ForMember(s => s.Features, opt =>
            {
                opt.MapFrom(s => Map_SampleFeature2SampleFeatureParam(s.Features));
            })
            .ReverseMap()
            .ForMember(s => s.Samples, opt =>
            {
                opt.MapFrom(s => Map_SampleCellParam2SampleCell(s.Samples));
            })
            .ForMember(s => s.Features, opt =>
            {
                opt.MapFrom(s => Map_SampleFeatureParam2SampleFeature(s.Features));
            });
        }));
        static SampleCell[] Map_SampleCellParam2SampleCell(SampleCellParam[] sampleCellParams) {
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleCellParams);
            return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleCell[]>(json);
        }
        static SampleCellParam[] Map_SampleCell2SampleCellParam(SampleCell[] sampleCells)
        {
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleCells);
            return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleCellParam[]>(json);
        }

        static SampleFeature[] Map_SampleFeatureParam2SampleFeature(SampleFeatureParam[] sampleFeatureParams)
        {
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleFeatureParams);
            return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleFeature[]>(json);
        }
        static SampleFeatureParam[] Map_SampleFeature2SampleFeatureParam(SampleFeature[] sampleFeatures)
        {
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleFeatures);
            return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleFeatureParam[]>(json);
        }
        public static bool Load(GSample src, string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    string json = File.ReadAllText(filePath);
                    var p = JsonConvert.DeserializeObject<GSampleJsonDb>(json);
                    Mapper.Map(p, src);
                    return true;
                }
            }
            catch
            {
                //异常,没有json 解码失败
            }

            return false;
        }
        public static bool Save(GSample src, string filePath)
        {
            var p = Mapper.Map<GSampleJsonDb>(src);
            try
            {
                File.WriteAllText(filePath, JsonConvert.SerializeObject(p, Formatting.Indented));
                return true;
            }
            catch
            {
                //异常,没有json 编码失败

            }
            return false;

        }
        public bool Enable { get; set; } = true;

		public int Window { get; set; }
        public int SampleRange { get; set; } = 100;
        public bool IsCheckByPercent { get; set; } = true;
        public double ErrPercent { get; set; } = 2;
        public int ErrValue { get; set; } = 200;
        public double CrossErrPercent { get; set; } = 0.02;
        public int ErrIntervalMin { get; set; } = 30;
        public SampleCellParam[] Samples { get; set; }
        public int SearchRange { get; set; } = 100;
        public SampleFeatureParam[] Features { get; set; }
    }
}