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;
using System.Reflection;
using FLY.OBJComponents.Common;

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 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;
        /// <summary>
        /// 采集状态
        /// </summary>
        public SampleState[] states;
        public enum SampleState
        {
            /// <summary>
            /// 准备采集
            /// </summary>
            ReadyToCapture,
            /// <summary>
            /// 采集成功
            /// </summary>
            Ok,
            /// <summary>
            /// 采集失败
            /// </summary>
            IsFailure
        }
        IGageInfoService mGageInfo;
        OBJComponents.Server.WarningSystem2 warningSystem;
        IFlyADClientAdv flyad;
        SampleData last_sampleData;

        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()];
            states = new SampleState[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 };

            if (!Load())
                Save();
        }
        public virtual void Init(
            IFlyADClientAdv 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)
        {
            last_sampleData = null;
            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;
                states[i] = SampleState.ReadyToCapture;
                tempFilters[i].Reset();
            }
        }
        /// <summary>
        /// 进入采集周期，设置全部采样点 为准备采集状态
        /// </summary>
        public void ReadyToCapture()
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                states[i] = SampleState.ReadyToCapture;
            }
        }
        /// <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();
                    states[i] = SampleState.IsFailure;
                }
            }
        }
        /// <summary>
        /// 设置全部样品为失败
        /// </summary>
        void SetFailure()
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                var tempre = tempFilters[i];
                if (sample.Enable)
                {
                    tempre.Reset();
                    states[i] = SampleState.IsFailure;
                }
            }
        }

        public bool HasSampleAd()
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                if (sample.Enable)
                {
                    if (sample.AD <= 0)
                        return false;
                }
            }
            return true;
        }
        enum GetSampleAdResult 
        {
            OK,
            /// <summary>
            /// 没有采集数据
            /// </summary>
            Invalid,
            /// <summary>
            /// AD值为0
            /// </summary>
            Zero,
            /// <summary>
            /// 滤波 异常
            /// </summary>
            FilterErr
        }
        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;
                    }
                }
            }
            return GetSampleAdResult.OK;
        }

        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;
                        states[i] = SampleState.Ok;
                    }
                    else
                    {

                        return ret;
                    }
                }
            }
            return GetSampleAdResult.OK;
        }
        void UpdateSampleErrNo(ERRNO sampleErrNo) 
        {
            this.warningSystem.Update(
                sampleErrNo.Code,
                sampleErrNo.Description
                );
        }
        /// <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)
            {
                UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_ParamErr);
                return;
            }
            int grid_offset = 0;//经过相识性计算后的偏移量

            //获取这个方向全部样品AD
            var ret = SetSampleAds(direction, grid_start, buf, grid_offset);
            switch (ret)
            {
                case GetSampleAdResult.Zero:
                    {
                        UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_ADZero);
                    }
                    break;
                case GetSampleAdResult.Invalid:
                    {
                        UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_NoData);
                    }
                    break;
                case GetSampleAdResult.FilterErr:
                    {
                        UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_FilterErr);
                    }
                    break;
            }

            if (ret != GetSampleAdResult.OK)
            {
                //有问题，全部复位
                SetFailure(direction);
            }
            if (OK())
            {
                //全部都采集完成
                if (!IsErr())//没有异常
                {
                    Remove_ERRNO_SAMPLE();

                    //备份
                    last_sampleData = new SampleData();
                    last_sampleData.Time = DateTime.Now;
                    last_sampleData.SampleADs = new int[Samples.Count()];
                    for (int i = 0; i < Samples.Count(); i++)
                    {
                        last_sampleData.SampleADs[i] = Samples[i].AD;
                    }
                }
            }
            _end:
            SampleChangedEvent?.Invoke(this);
        }
        void Remove_ERRNO_SAMPLE()
        {
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_ParamErr);
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_ADZero);
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_NoData);
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_FilterErr);
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_PercentErr);
            this.warningSystem.Remove(ERRNOs.Instance.ERRNO_SAMPLE_CrossErr);
        }
        bool IsErr()
        {
            if (last_sampleData == null)
                return false;

            if (IsPercentErr())
            {
                //历史比较有问题
                UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_PercentErr);

                //设置全部都是异常，重新测量
                SetFailure();
                last_sampleData = null;
                return true;
            }

            if (IsCrossErr())
            {
                //交叉比较有问题
                UpdateSampleErrNo(ERRNOs.Instance.ERRNO_SAMPLE_CrossErr);

                //设置全部都是异常，重新测量
                SetFailure();
                last_sampleData = null;
                return true;
            }

            return false;
        }

        /// <summary>
        /// 检测是否成功
        /// </summary>
        /// <returns></returns>
        public virtual bool OK()
        {
            return !IsFailure();
        }

        /// <summary>
        /// 刚才报警了，采样失败
        /// </summary>
        /// <returns></returns>
        public bool IsFailure() 
        {
            if (Enable)
            {
                for (int i = 0; i < Samples.Count(); i++)
                {
                    SampleCell sample = Samples[i];
                    if (sample.Enable)
                    {
                        if (states[i] != SampleState.Ok)
                            return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 全部采集完成，与上次采集比较 大于2% 异常
        /// </summary>
        /// <returns></returns>
        bool IsPercentErr()
        {
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                if (sample.Enable)
                {
                    int ad = sample.AD;
                    if (ad <= 0)//写错程序
                        break;
                    int lastAd = last_sampleData.SampleADs[i];
                    double diff = Math.Abs(ad - lastAd);
                    if (diff / ad > ErrPercent) //大于2% 异常
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 全部采集完成，再做 交叉比较
        /// </summary>
        /// <returns></returns>
        bool IsCrossErr()
        {
            //交叉比较
            for (int i = 1; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i];
                if (sample.Enable)
                {
                    double p = 1.0 * sample.AD / Samples[0].AD;
                    if (p <= 0)//写错程序
                        break;

                    int lastAdi = last_sampleData.SampleADs[i];
                    int lastAd0 = last_sampleData.SampleADs[0];
                    if (lastAd0 <= 0)//写错程序
                        break;

                    double last_p = 1.0 * lastAdi / lastAd0;
                    double diff = Math.Abs(last_p - p);

                    if (diff / p > CrossErrPercent) //大于2% 异常
                    {
                        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 class SampleData : INotifyPropertyChanged
    {
        public DateTime Time { get; set; }
        /// <summary>
        /// 样品采集AD值
        /// </summary>
        public int[] SampleADs { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    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 SampleCellParam[] Samples { get; set; }
        public int SearchRange { get; set; } = 100;
        public SampleFeatureParam[] Features { get; set; }
    }
}
