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;

namespace FLY.Thick.Base.Server
{
    /// <summary>
    /// 样品点
    /// </summary>
    public class SampleCell:ISampleCell, ISaveToXml
    {

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

        /// <summary>
        /// 参数：只检查不标定
        /// </summary>
        public bool JustForCheck { get; set; }

        /// <summary>
        /// 参数：原始AD值
        /// </summary>
        public int OrgAD { get; set; }
        
        /// <summary>
        /// 参数：位置
        /// </summary>
        public int Position { get; set; }
        
        /// <summary>
        /// 状态：当前测量的AD值
        /// </summary>
        public int AD { get; set; }


        /// <summary>
        /// 状态：当前测量的AD值 对应的 样品值
        /// </summary>
        public int SampleValue { get; set; }

        /// <summary>
        /// 状态：方向 ,GSample 计算赋值的！！！
        /// </summary>
        public Misc.DIRECTION Direction;
        /// <summary>
        /// 滤波
        /// </summary>
        public TempFilter TempF=new TempFilter();

        #region ISaveToXml 成员

        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "Enable",
                "JustForCheck",
                "OrgAD",
                "Position"
            };
        }

        #endregion
        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
        
    }
    /// <summary>
    /// 样品正向校正特征
    /// </summary>
    public class SampleFeature : ISampleFeature, ISaveToXml
    {

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


        /// <summary>
        /// 参数：开始位置
        /// </summary>
        public int StartPos { get; set; }


        /// <summary>
        /// 参数：结束位置
        /// </summary>
        public int EndPos { get; set; }


        /// <summary>
        /// 状态：相似度必须高于0.8，失败-1
        /// </summary>
        public double MaxRelevancy { get; set; } = -1;

        /// <summary>
        /// 状态：最大相似度对应的偏移
        /// </summary>
        public int MaxOffset { get; set; }

        /// <summary>
        /// 状态：扫描数据，从扫描数据提取的相似的一段
        /// 它的单位是 grid, 需要 posOfGrid, 转换为 pos
        /// 开始位置是 StartPos - Search
        /// </summary>
        [DoNotCheckEquality]
        public int[] ScanData { get; set; }
        

        #region ISaveToXml 成员

        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "Enable",
                "StartPos",
                "EndPos"};
        }

        #endregion

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;


        #endregion
    }

    public delegate void SampleChangedEventHandler(GSample gsample);

    public class GSample : IGetSampleService, Misc.ISaveToXml
    {
        #region IGetSampleService 接口

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

        /// <summary>
        /// 参数：间隔
        /// </summary>
        public int Interval { get; set; }


        /// <summary>
        /// 采样计数
        /// </summary>
        public int Timer { get; set; }
        
        /// <summary>
        /// 参数：速度
        /// </summary>
        public UInt32 Velocity { get; set; }
        
        
        /// <summary>
        /// 参数：样品点范围
        /// </summary>
        public int Range { get; set; }


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


        /// <summary>
        /// 样品
        /// </summary>
        public ISampleCell[] Samples { get; } = new ISampleCell[3];

        /// <summary>
        /// 查找公差
        /// </summary>
        public int Search { get; set; }

        /// <summary>
        /// 特征  相识度 0 正， 1 反
        /// </summary>
        public ISampleFeature[] Features { get; } = new ISampleFeature[2];


        /// <summary>
        /// 状态：从flyad7 得到的。 用于绘制图
        /// </summary>
        public int PosOfGrid { get; set; } = 10;
    

        #endregion

        IGageInfoService mGageInfo;

        FlyAD7 flyad;

        public event SampleChangedEventHandler SampleChangedEvent;
        private string param_path;
        public GSample(FlyAD7 flyad, IGageInfoService gageInfo):this(flyad,gageInfo,"gsample.xml")
        {

        }
        public GSample(FlyAD7 flyad, IGageInfoService gageInfo, string param_path) 
        {
            this.param_path = param_path;
            this.flyad = flyad;
            this.mGageInfo = gageInfo;
            Misc.BindingOperations.SetBinding(flyad, "PosOfGrid", this, "PosOfGrid");

            Enable = true;
            Interval = 5;//采样间隔
            Range = 100;//样品范围
            Velocity = 200;//取样速度
            Window = 3;//移动窗口
            Samples[0] = new SampleCell()
            {
                Enable = true,
                Position = 0,
                OrgAD = 0,
                Direction = Misc.DIRECTION.BACKWARD
            };
            Samples[1] = new SampleCell()
            {
                Enable = false,
                Position = 2006,
                OrgAD = 30000,
                Direction = Misc.DIRECTION.FORWARD
            };
            Samples[2] = new SampleCell()
            {
                Enable = false,
                Position = 0,
                OrgAD = 0,
                Direction = Misc.DIRECTION.FORWARD
            };

            foreach (SampleCell sample in Samples)
                sample.TempF.SetWindow(Window);


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

            this.PropertyChanged += new PropertyChangedEventHandler(GSample_PropertyChanged);
            Load();            
        }

        void GSample_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Window") 
            {
                foreach(SampleCell sample in Samples)
                {
                    sample.TempF.SetWindow(Window);
                }
            }
        }

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

        public void Save() 
        {
            Misc.SaveToXmlHepler.Save(param_path, this);
        }
        /// <summary>
        /// 1.判断样品取样方向
        /// 2.复位间隔计数
        /// 3.复位所有样品取样状态
        /// </summary>
        /// <param name="scanbegin"></param>
        /// <param name="scanend"></param>
        public virtual void Init(int scanbegin, int scanend)
        {
            foreach(SampleCell sample in Samples)
            {
                int pos = sample.Position;
                if (Math.Abs(pos - scanbegin) < Math.Abs(pos - scanend))
                    sample.Direction = Misc.DIRECTION.BACKWARD;
                else
                    sample.Direction = Misc.DIRECTION.FORWARD;
                sample.AD = -1;
                sample.TempF.ResetSampleFailure();
            }

            Timer = Interval;
        }
        public void Init() 
        {
            foreach (SampleCell sample in Samples)
            {
                sample.AD = -1;
                sample.TempF.ResetSampleFailure();
            }
            Timer = Interval;
        }
        /// <summary>
        /// 检测是否要样品校正
        /// </summary>
        /// <returns></returns>
        public virtual bool Check()
        {
            if (!Samples.Any(s => s.Enable))
            {
                return false;
            }

            if (Timer < Interval)
            {
                Timer++;
                return false;
            }
            else
            {
                return true;
            }
        }
        /// <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 = 0;
            if (direction == Misc.DIRECTION.BACKWARD)
                r_idx = 1;

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

            int i;
            foreach(SampleCell sample in Samples)
            {
                if ((sample.Enable) && (sample.Direction == 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>
        public virtual void Do(Misc.DIRECTION direction, int grid_start, int[] buf)
        {
            bool isFailure = false;//取样失败了

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

            //在这里处理gsample->m_timer
            int dir_idx = 0;
            if (direction == Misc.DIRECTION.FORWARD)
                dir_idx = 0;
            else
                dir_idx = 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[] gagedata;
                if (direction == Misc.DIRECTION.FORWARD)
                    gagedata = mGageInfo.ForwData.ToArray();
                else
                    gagedata = mGageInfo.BackwData.ToArray();


                //合格,查找最大相识性的点
                int grid_b = grid1 - grid_searchtol;
                int grid_e = grid2 + grid_searchtol;
                int grid_len = grid_e - grid_b + 1;
                psr.ScanData = new int[grid_len];
                Array.Copy(buf, grid_b - grid_start, psr.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, psr.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;
                TempFilter tempre = sample.TempF;
                if ((sample.Direction == 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
                        {
                            ad = tempre.CalSampleAD(ad);//ad filter	
                            if (tempre.IsSampleFailure())//AD突变。。。异常，复位
                            {
                                isFailure = true;
                            }
                        }
                    }
                    if (isFailure)
                    {
                        tempre.ResetSampleFailure();
                        ad = -1;
                    }

                    sample.AD = ad;
                }
            }



            if (SampleChangedEvent != null)
                SampleChangedEvent(this);


            if (isFailure)//样品获取失败，下次继续！！！
                    return;
            //只有当所有都成功，Timer=0
            if(Samples.All(s => (s.Enable && s.AD > 0) || (!s.Enable)))
                Timer = 0;
        }

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

            return true;
        }

        /// <summary>
        /// 获取N秒的数据，
        /// </summary>
        /// <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] as SampleCell;
            sample.AD = avg;

            if (SampleChangedEvent != null)
                SampleChangedEvent(this);


            //只有当所有都成功，Timer=0
            if (Samples.All(s => (s.Enable && s.AD > 0) || (!s.Enable)))
                Timer = 0;
        }
        /// <summary>
        /// 重新获取样品
        /// </summary>
        //public void Reset()
        //{
        //    foreach (SampleCell sample in Samples) 
        //    {
        //        TempFilter tempre = sample.TempF;
        //        tempre.ResetSampleFailure();
        //    }
        //}


        #region ISaveToXml 成员

        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "Enable",
                "Interval",
                "Range",
                "Window",
                "OnePointIsOffset",
                "Samples",
                "Search",
                "Features"
            };
        }

        static GSample() 
        {
            Misc.SaveToXmlHepler.Regist(typeof(GSample));
            Misc.SaveToXmlHepler.Regist(typeof(SampleCell));
            Misc.SaveToXmlHepler.Regist(typeof(SampleFeature));
        }
        #endregion
        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }

        #endregion


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