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>
        /// 参数：样品点范围
        /// </summary>
        public int Range { 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; } = 2;
        /// <summary>
        /// 异常值
        /// </summary>
        public int ErrValue { get; set; } = 200;

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

        /// <summary>
        /// 查找公差
        /// </summary>
        public int Search { 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;
            Range = 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;

            Search = 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 - Range;
                    int _e = sample.Position + Range;

                    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 -= Search;
                    e += Search;
                }
                start = b;
                end = e;
            }

            return valid;
        }


        /// <summary>
        /// 检测参数合法性
        /// </summary>
        /// <returns></returns>
        protected bool CheckParamIsValid() 
        {
            //样品位置 要在 范围
            foreach(SampleCell sample in Samples)
            {
                if (sample.Enable)
                {                    
                    if(((sample.Position -  Range)<0) || ((sample.Position+Range)>= flyad.PosLen))
                        return false;
                }
            }
            foreach(SampleFeature feature in Features)
            {
                if (feature.Enable)
                {                    
                    if(((feature.StartPos -  Search)<0) || ((feature.EndPos+Search)>= 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);
        }


        /// <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)
        {
            //取样结果，isFailure=true, 取样失败，全部复位！！！
            bool isFailure = false;

            if (CheckParamIsValid() == false)
                return;
            int grid_offset = 0;//经过相识性计算后的偏移量

            //在这里处理gsample->m_timer
            int dir_idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;

            //数据在 gsample -> gagedata[0]
            SampleFeature psr = Features[dir_idx] as SampleFeature;

            //这个方向的特征查找使能了，位置修正！！！！！！
            if (psr.Enable)
            {
                //检测机架数据，当为空时，不能作相识性计算
                if (!mGageInfo.DataOK)
                    goto _corr_end;

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

                int grid1 = pos1 / posOfGrid;
                int grid2 = pos2 / posOfGrid;
                int grid_searchtol = Search / 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;//位置修正
                else
                {
                    //相似性太低，取样失败！！！
                    isFailure = true;
                }
            }

            _corr_end:
            int ad = -1;
            for (int i = 0; i < Samples.Count(); i++)
            {
                SampleCell sample = Samples[i] as SampleCell;
                var tempre = tempFilters[i];
                if ((directions[i] == direction) && (sample.Enable))
                {
                    ad = -1;
                    if (!isFailure)//不修要位置修正
                    {
                        //获取grid 数据
                        int posOfGrid = flyad.PosOfGrid;
                        int grid = sample.Position / posOfGrid + grid_offset;
                        int grid_range = Range / 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无效。。。异常，复位
                        {
                            isFailure = true;
                        }
                        else
                        {
                            int orgAd = ad;
                            ad = tempre.CalSampleAD(orgAd, TimeSpan.FromMinutes(Window));//ad 滤波
                            int diff = Math.Abs(orgAd - ad);
                            if (IsCheckByPercent)
                            {
                                if (diff == 0)
                                {

                                }
                                else if (ad == 0)
                                {
                                    isFailure = true;
                                }
                                else if (diff / ad >= ErrPercent * 0.01) //大于2% 异常
                                {
                                    isFailure = true;
                                }
                            }
                            else {
                                if (diff >= ErrValue)
                                {
                                    isFailure = true;
                                }
                            }
                        }
                    }
                    if (isFailure)
                    {
                        tempre.Reset();
                        ad = -1;
                    }
                    else
                    {
                        sample.AD = ad;
                    }
                    sample.IsFailure = isFailure;
                }
            }

            SampleChangedEvent?.Invoke(this);


            if (isFailure)//样品获取失败，下次继续！！！
            {
                
                this.warningSystem.Add(
                    ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code,
                    ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption);
            }
        }

        /// <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);
        }
        /// <summary>
        /// 重新获取样品
        /// </summary>
        //public void Reset()
        //{
        //    foreach (SampleCell sample in Samples) 
        //    {
        //        TempFilter tempre = sample.TempF;
        //        tempre.ResetSampleFailure();
        //    }
        //}


        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 asyncCB, 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());
                else
                    ret.Add(null);
            }
            asyncCB(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 Range { get; set; } = 100;
        public int Window { get; set; }
        public bool IsCheckByPercent { get; set; } = true;
        public double ErrPercent { get; set; } = 2;
        public int ErrValue { get; set; } = 200;
        public SampleCellParam[] Samples { get; set; }
        public int Search { get; set; } = 100;
        public SampleFeatureParam[] Features { get; set; }
    }
}
