using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Misc;
using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;

namespace FLY.Thick.Base.Server
{

    /// <summary>
    /// 边界查找
    /// </summary>
    public class BorderSearch : IBorderSearchService, INotifyPropertyChanged, Misc.ISaveToXml
    {
        /// <summary>
        /// 报警测试
        /// </summary>
        public static FLY.OBJComponents.Common.ERRNO BORDER_ERRNO_NOFILM = new FLY.OBJComponents.Common.ERRNO() { Code = 50, Descrption = "边界异常,没有被测物" };

        #region 延时执行，Markno
        public const int MARKNO_SAVE = 1;
        #endregion
        #region 属性，成员变量的代理

        /// <summary>
        /// 启动与否
        /// </summary>
        public bool Enable { get; set; }

        /// <summary>
        /// 边界拐点检测，找到的边界更加精确
        /// </summary>
        public bool IsBreakDetect { get; set; } = true;

        protected Range[] border = new Range[2] { new Range(), new Range() };

        /// <summary>
        /// 有限范围
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public Range Valid { get; set; } = new Range();
        /// <summary>
        /// 边界
        /// </summary>
        public Range Border_Forw { get { return border[0]; } }
        /// <summary>
        /// 边界
        /// </summary>
        public Range Border_Backw { get { return border[1]; } }

        /// <summary>
        /// 膜范围
        /// </summary>
        public Range Border { get; set; } = new Range();

        /// <summary>
        /// 当前膜宽，测量出来的，单位是 脉冲
        /// </summary>
        public int Width { get; protected set; }

        /// <summary>
        /// 膜中间位置 单位 脉冲
        /// </summary>
        public int Mid { get; protected set; }
        
        /// <summary>
        /// 手动设置温修AD值
        /// </summary>
        public bool TempADBySet { get; set; }

        /// <summary>
        /// 温修AD值
        /// </summary>
        public int CurrTempAD { get; set; } = 50000;

        /// <summary>
        /// 温修AD值
        /// </summary>
        public int TempAD { get; set; } = 50000;

        /// <summary>
        /// AD超过了范围， 就认为开始找到边界
        /// </summary>
        public int TempRange { get; set; } = 1000;

        /// <summary>
        /// 温修范围是温修的百分比
        /// </summary>
        public bool IsTempRangeByPercent { get; set; } = true;

        /// <summary>
        ///  温修范围百分比
        /// </summary>
        public double TempRangePercent { get; set; } = 0.02;


        /// <summary>
        /// 有滤波器，只有非空的连续N个pos以上，才开始算边界开始
        /// </summary>
        public int N { get; set; }

        /// <summary>
        /// 探头直径，单位脉冲， 膜宽 = 边界范围 - 探头直径
        /// </summary>
        public int SensorWidth { get; set; }




        /// <summary>
        /// 找到边界后，再往内缩N2个脉冲，防止边界找得不准的问题。
        /// </summary>
        public int N2 { get; set; }


        /// <summary>
        /// 记录两个边界以后扫描，以它们再外扩N3个脉冲，作为扫描范围 
        /// </summary>
        public int N3 { get; set; }


        /// <summary>
        /// 用于调试， 进入到边界查找的数据
        /// </summary>
        public int[] Datas { get; set; } = null;

        #endregion

        OBJComponents.Server.WarningSystem warningSystem;
        private string param_path = "bordersearch.xml";
        public BorderSearch()
        {
            Default();
            this.PropertyChanged += new PropertyChangedEventHandler(BorderSearch_PropertyChanged);


        }

        public BorderSearch(string param_path) 
        {
            if (!string.IsNullOrEmpty(param_path))
                this.param_path = param_path;
            Default();
            this.PropertyChanged += new PropertyChangedEventHandler(BorderSearch_PropertyChanged);

            this.Border_Forw.PropertyChanged += (s, e) => {
                NotifyPropertyChanged("Border_Forw");
            };
            this.Border_Backw.PropertyChanged += (s, e) => {
                NotifyPropertyChanged("Border_Backw");
            };
            this.Border.PropertyChanged += (s, e) => {
                NotifyPropertyChanged("Border");
            };
            Load();
        }

        public void Init(OBJComponents.Server.WarningSystem warningSystem) 
        {
            this.warningSystem = warningSystem;
        }
        void Default() 
        {
            Enable = false;
            Valid = new Range() { Begin = 1200, End = 7400 };
            
            Init();

            TempAD = -1;
            N = 20;
            N2 = 400;
            N3 = 1000;
            SensorWidth = 250;
            Width = 0;
            Border_Backw.Reset();
            Border_Forw.Reset();
            Border.Reset();
            Mid = Valid.Mid;
            TempRange = 500;
            TempRangePercent = 500.0 / 50000;
            IsTempRangeByPercent = true;
        }




        void BorderSearch_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrTempAD")
            {
                if (!TempADBySet)
                    TempAD = CurrTempAD;
            }
            else if (e.PropertyName == "TempAD")
            {
                if (IsTempRangeByPercent)
                {
                    TempRange = (int)(TempAD * TempRangePercent);
                }
            }
            else if ((e.PropertyName == "TempRangePercent") || (e.PropertyName == "IsTempRangeByPercent"))
            {
                if (IsTempRangeByPercent)
                {
                    TempRange = (int)(TempAD * TempRangePercent);
                }
            }

        }

        public void Init()
        {
            Border_Backw.Reset();
            Border_Forw.Reset();
            Width = 0;
            Border.Reset();
            Mid = Valid.Mid;
        }
        public Range GetScanRange() 
        {
            int b, e;
            GetScanRange(out b, out e);
            return new Range() { Begin = b, End = e };
        }
        public void GetScanRange(out int posBegin, out int posEnd)
        {
            bool isForwFindOut=false;
            bool isBackFindOut=false;

            if (Border_Forw.Begin != Misc.MyBase.NULL_VALUE)
            {
                //正向找到边界
                isForwFindOut = true;
            }

            if (Border_Backw.Begin != Misc.MyBase.NULL_VALUE) 
            {
                //反向找到边界
                isBackFindOut = true;
            }
            int min=Valid.Begin;
            int max=Valid.End;

            if(isForwFindOut && isBackFindOut)
            {

                min = Math.Min(Border_Forw.Begin, Border_Backw.Begin) - N3;
                max = Math.Max(Border_Forw.End, Border_Backw.End) + N3;
            }
            else if(isForwFindOut)
            {
                min = Border_Forw.Begin - N3;
                max = Border_Forw.End + N3;
            }
            else if(isBackFindOut)
            {
                min = Border_Backw.Begin-N3;
                max = Border_Backw.End + N3;
            }
            //else
            //{
            //    //都不能找到边界
            //}
            
            //范围限制
            if (min < Valid.Begin)
                min = Valid.Begin;

            if (max > Valid.End)
                max = Valid.End;
            posBegin = min;
            posEnd = max;
        }
        private bool IsNULL(int dat, bool pre)//判断是否为空
        {
            if (TempAD != Misc.MyBase.NULL_VALUE)//以温修作为空的数据
            {
                if (!Misc.MyBase.ISVALIDATA(dat))//当数据无效时，按上一次的结果作准 
                {
                    return pre;
                }
                if (Math.Abs(TempAD - dat) < TempRange)
                {
                    return true;
                }
            }
            else//以数据无效作为空的数据
            {
                if (!Misc.MyBase.ISVALIDATA(dat))//当数据有效 ，那就是边界
                    return true;
            }
            return false;
        }



        /// <summary>
        /// 初略操作，超过范围 就是边界
        /// </summary>
        /// <param name="dat"></param>
        /// <param name="validbegin_grid"></param>
        /// <param name="validend_grid"></param>
        /// <param name="tempad"></param>
        /// <param name="temprange"></param>
        /// <param name="borderbegin_grid"></param>
        /// <param name="borderend_grid"></param>
        /// <returns></returns>
        protected bool FindFilm_Threshold(int[] dat, int validbegin_grid, int validend_grid, int tempad, int temprange, out int borderbegin_grid, out int borderend_grid) 
        {
            borderbegin_grid = -1;
            borderend_grid = -1;

            ////找左边界 
            //for (int i = 0 ; i < dat.Length; i++)
            //{
            //    if ((i >= validbegin_grid) && (i <= validend_grid))
            //    {
            //        if (Misc.MyBase.ISVALIDATA(dat[i]))
            //        {
            //            if (Math.Abs(TempAD - dat[i]) > TempRange)
            //            {
            //                //膜
            //                borderbegin_grid = i;
            //                break;
            //            }
            //        }
            //    }
            //}
            //if (borderbegin_grid == -1)//没有膜
            //    return false;

            ////找右边界 
            //for (int i = dat.Length - 1; i >= borderbegin_grid; i--)
            //{
            //    if ((i >= validbegin_grid) && (i <= validend_grid))
            //    {
            //        if (Misc.MyBase.ISVALIDATA(dat[i]))
            //        {
            //            if (Math.Abs(TempAD - dat[i]) > TempRange)
            //            {
            //                //膜
            //                borderend_grid = i;
            //                break;
            //            }
            //        }
            //    }
            //}
            //if (borderend_grid == -1)//没有膜
            //    return false;


            int mid_grid = (validbegin_grid + validend_grid) / 2;

            //从中间开始向左边找
            //必须先找到膜
            for (int i = mid_grid; i >= validbegin_grid; i--)
            {
                if (i >= 0 && i < dat.Length)
                {
                    if (Misc.MyBase.ISVALIDATA(dat[i]))
                    {
                        if (Math.Abs(TempAD - dat[i]) > TempRange)
                        {
                            //膜
                            borderbegin_grid = i;
                        }
                        else
                        {
                            //空气
                            if (borderbegin_grid != -1)
                            {
                                //之前有膜
                                break;
                            }
                        }
                    }
                }
                else if (i < 0)
                {
                    break;
                }
            }

            if (borderbegin_grid == -1)//没有膜
            {
                //从左边开始找
                //找左边界 ,找阀值点
                for (int i = mid_grid; i <= validend_grid; i++)
                {
                    if (i >= 0 && i < dat.Length)
                    {
                        if (Misc.MyBase.ISVALIDATA(dat[i]))
                        {
                            if (Math.Abs(TempAD - dat[i]) > TempRange)
                            {
                                //膜
                                borderbegin_grid = i;
                                break;
                            }
                        }
                    }
                }
                if (borderbegin_grid == -1)//没有膜
                    return false;
            }
            
            //从左边界开始找
            for (int i = borderbegin_grid; i <= validend_grid; i++)
            {
                if (i >= 0 && i < dat.Length)
                {
                    if (Misc.MyBase.ISVALIDATA(dat[i]))
                    {
                        if (Math.Abs(TempAD - dat[i]) > TempRange)
                        {
                            //膜
                            borderend_grid = i;
                        }
                        else
                        {
                            //空气
                            if (borderend_grid != -1)
                            {
                                //之前有膜
                                break;
                            }
                        }
                    }
                }
            }

            if (borderend_grid == -1)//没有膜
                return false;

            return true;
        }
        /// <summary>
        /// 精细操作，再向外找 折点
        /// </summary>
        /// <param name="dat"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="borderbegin_grid"></param>
        /// <param name="borderend_grid"></param>
        /// <returns></returns>
        bool FindFilm_Break(int[] dat, int sensor_grid, int filter_grid, ref int borderbegin_grid, ref int borderend_grid)
        {
            //当前肯定在斜波上
            //边界肯定在 粗边界以外 探头半径 内
            //int N4 = SensorWidth / posOfGrid;

            if (sensor_grid <= 2)
            {
                //不用找，传感器直径非常小，已经很准确了
                return true;
            }

            //int num = N / posOfGrid / 2;//滤波半径
            int num = filter_grid / 2;
            if (num < 1) num = 1;


            int[] filterdats = new int[dat.Length];//数据滤波
            for (int i = 0; i < dat.Length; i++)
            {
                int b = i - num;
                int e = i + num;
                if (b < 0)
                {
                    b = 0;
                    e = b + num * 2;
                }
                else if (e >= (dat.Length - 1))
                {
                    e = dat.Length - 1;
                    b = e - num * 2;
                }
                filterdats[i] = Misc.MyMath.Avg(dat, b, e);
            }
            int bg = borderbegin_grid;
            int eg = borderend_grid;

            int[] breakdats = new int[dat.Length];
            num = 1;

            for (int i = 0; i < dat.Length; i++)
            {
                if ((i - num) < 0)
                    breakdats[i] = Misc.MyBase.NULL_VALUE;
                else if ((i + num) >= dat.Length)
                    breakdats[i] = Misc.MyBase.NULL_VALUE;
                else if (!Misc.MyBase.ISVALIDATA(filterdats[i - num]))
                    breakdats[i] = Misc.MyBase.NULL_VALUE;
                else if (!Misc.MyBase.ISVALIDATA(filterdats[i + num]))
                    breakdats[i] = Misc.MyBase.NULL_VALUE;
                else if (!Misc.MyBase.ISVALIDATA(filterdats[i]))
                    breakdats[i] = Misc.MyBase.NULL_VALUE;
                else
                {
                    breakdats[i] = Math.Abs((filterdats[i] - filterdats[i - num]) * (filterdats[i + num] - filterdats[i]));
                }
            }

            bool isdown = false;
            //从第1次找到的边界开始，往外找，找折点的最小值
            for (int i = borderbegin_grid; i >= (borderbegin_grid - sensor_grid); i--)
            {
                if ((i - num) < 0)
                    break;
                if ((i + num) >= dat.Length)
                    break;
                if (!Misc.MyBase.ISVALIDATA(breakdats[i - num]))
                    break;
                if (!Misc.MyBase.ISVALIDATA(breakdats[i + num]))
                    break;
                if (!Misc.MyBase.ISVALIDATA(breakdats[i]))
                    break;
                if (breakdats[i] < breakdats[i + num])
                {
                    //在减少
                    bg = i;
                    isdown = true;
                }
                if (isdown)
                {
                    if (breakdats[i] < breakdats[i - num])
                    {
                        //之前那个就是最小值
                        break;
                    }
                }
            }
            isdown = false;
            for (int i = borderend_grid; i <= (borderend_grid + sensor_grid); i++)
            {
                if ((i - num) < 0)
                    break;
                if ((i + num) >= dat.Length)
                    break;
                if (!Misc.MyBase.ISVALIDATA(filterdats[i - num]))
                    break;
                if (!Misc.MyBase.ISVALIDATA(filterdats[i + num]))
                    break;
                if (!Misc.MyBase.ISVALIDATA(filterdats[i]))
                    break;

                if (breakdats[i] < breakdats[i - num])
                {
                    //在减少
                    eg = i;
                    isdown = true;
                }
                if (isdown)
                {
                    if (breakdats[i] < breakdats[i + num])
                    {
                        //之前那个就是最小值
                        break;
                    }
                }
            }
            borderbegin_grid = bg;
            borderend_grid = eg;
            return true;
        }
        /// <summary>
        /// 放入grid数据，分析边界
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridBegin"></param>
        /// <param name="dat"></param>
        /// <returns></returns>
        public virtual bool FindFilm(DIRECTION direction, int posOfGrid, int gridBegin, int[] dat)
        {
            //把数据记录下来
            if ((Datas == null)|| (Datas.Length < (dat.Length + gridBegin)))
            {
                int len = dat.Length + gridBegin;
                Datas = new int[len];
            }
            for (int i = 0; i < Datas.Length; i++)
            {
                if (i < gridBegin)//清空前面
                    Datas[i] = Misc.MyBase.NULL_VALUE;
                else if (i < (gridBegin + dat.Length))
                    Datas[i] = dat[i - gridBegin];
                else//清空后面
                    Datas[i] = Misc.MyBase.NULL_VALUE;
            }
            NotifyPropertyChanged("Datas");

            //转为 dat 的序号
            int validbegin_grid = Valid.Begin / posOfGrid - gridBegin;
            int validend_grid = Valid.End / posOfGrid - gridBegin;
            
            int borderbegin_grid;//validbegin_grid;
            int borderend_grid;//validend_grid;

            if (!FindFilm_Threshold(dat, validbegin_grid, validend_grid, TempAD, TempRange, out borderbegin_grid, out borderend_grid))
            {
                warningSystem.Add(BORDER_ERRNO_NOFILM.Code, BORDER_ERRNO_NOFILM.Descrption, OBJComponents.Common.ERR_STATE.ON);
                return false;
            }
            //N2 只是用来判断一下膜够不够宽而已，没用
            int N2_grid = (N2*3+SensorWidth) / posOfGrid;
            
            if ((borderend_grid - borderbegin_grid) < N2_grid)//膜太短
                return false;

            if (IsBreakDetect)
            {
                int sensor_grid = SensorWidth / posOfGrid;
                int filter_grid = N / posOfGrid;
                //精细查找
                FindFilm_Break(dat, sensor_grid, filter_grid, ref borderbegin_grid, ref borderend_grid);
            }

            int idx = (direction == DIRECTION.FORWARD) ? 0 : 1;

            //转为 脉冲
            border[idx].Begin = (borderbegin_grid + gridBegin) * posOfGrid;
            border[idx].End = (borderend_grid +gridBegin) * posOfGrid;

            if (Misc.MyBase.ISVALIDATA(border[0].Begin) && Misc.MyBase.ISVALIDATA(border[1].Begin)) 
            {
                Border.Begin = (border[0].Begin + border[1].Begin) / 2 + SensorWidth / 2;
                Border.End = (border[0].End + border[1].End) / 2 - SensorWidth / 2;

                Width = Border.Width;
                Mid = Border.Mid;
            }
            else if (Misc.MyBase.ISVALIDATA(border[0].Begin))
            {
                Border.Copy(border[0]);

                Width = Border.Width;
                Mid = Border.Mid;
            }
            else if (Misc.MyBase.ISVALIDATA(border[1].Begin))
            {
                Border.Copy(border[1]);

                Width = Border.Width;
                Mid = Border.Mid;
            }
            //膜宽

            return true;
        }
        
        /// <summary>
        /// 获取膜边界，当根本找不到膜时，返回找边界范围！！！
        /// 膜的边界，只是斜波的中间而已。
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="posBegin"></param>
        /// <param name="posEnd"></param>
        public void GetBorder(DIRECTION direction, out int posBegin, out int posEnd) 
        {
            if (direction == DIRECTION.FORWARD)
            {
                posBegin = Border_Forw.Begin;
                posEnd = Border_Forw.End;
                if (!Misc.MyBase.ISVALIDATA(posBegin))
                {
                    posBegin = Border_Backw.Begin;
                    posEnd = Border_Backw.End;
                }
            }
            else 
            {
                posBegin = Border_Backw.Begin;
                posEnd = Border_Backw.End;
                if (!Misc.MyBase.ISVALIDATA(posBegin))
                {
                    posBegin = Border_Forw.Begin;
                    posEnd = Border_Forw.End;
                }
            }
            if (Misc.MyBase.ISVALIDATA(posBegin)) 
            {
                posBegin += SensorWidth / 2;
                posEnd -= SensorWidth / 2;
            }
        }
        /// <summary>
        /// 获取膜边界，当根本找不到膜时，返回IsVaild=false 的Range！！！
        /// 膜的边界，只是斜波的中间而已。
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public Range GetBorder(DIRECTION direction) 
        {
            int b, e;
            GetBorder(direction, out b, out e);
            return new Range() { Begin = b, End = e };
        }
        
        /// <summary>
        /// 获取 膜边界 向内缩 N2 + SensorWidth / 2
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="posBegin"></param>
        /// <param name="posEnd"></param>
        public void GetBoltRange(DIRECTION direction, out int posBegin, out int posEnd) 
        {
            GetBorder(direction, out posBegin, out posEnd);
            if (Misc.MyBase.ISVALIDATA(posBegin)) 
            {
                posBegin += (SensorWidth / 2 + N2);
                posEnd -= (SensorWidth / 2 + N2);
            }
        }
        /// <summary>
        /// 膜边界 向内缩 N2 + SensorWidth / 2
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public Range GetBoltRange(DIRECTION direction) 
        {
            int b, e;
            GetBoltRange(direction, out b, out e);
            return new Range() { Begin = b, End = e };
        }

        /// <summary>
        /// 获取 膜边界 向内缩 N2
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="posBegin"></param>
        /// <param name="posEnd"></param>
        public void GetBoltRange2(DIRECTION direction, out int posBegin, out int posEnd)
        {
            GetBorder(direction, out posBegin, out posEnd);
            if (Misc.MyBase.ISVALIDATA(posBegin))
            {
                posBegin += N2;
                posEnd -= N2;
            }
        }
        /// <summary>
        /// 膜边界 向内缩 N2
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public Range GetBoltRange2(DIRECTION direction)
        {
            int b, e;
            GetBoltRange2(direction, out b, out e);
            return new Range() { Begin = b, End = e };
        }
        public bool Load() 
        {
            return Misc.SaveToXmlHepler.Load(param_path, this);
        }
        public bool Save() 
        {
            return Misc.SaveToXmlHepler.Save(param_path, this);
        }

        public void Apply() 
        {
            Save();
        }
        #region INotifyPropertyChanged 成员

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

        #endregion

        #region ISaveToXml 成员

        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "Enable",
                "Valid",
                "TempADBySet",
                "TempAD",
                "TempRange",
                "TempRangePercent",
                "IsTempRangeByPercent",
                "N",
                "SensorWidth",
                "N2",
                "N3",
                "IsBreakDetect"
            };
        }

        #endregion
    }
}
