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;
using FObjBase;
using AutoMapper;
using System.IO;
using Newtonsoft.Json;

namespace FLY.Thick.Base.Server
{

    /// <summary>
    /// 边界查找
    /// </summary>
    public class BorderSearch : IBorderSearchService, INotifyPropertyChanged
    {
        /// <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;



        /// <summary>
        /// 有效范围
        /// </summary>
        public RangeStruct Valid { get; set; } = RangeStruct.InvalidValue;
        /// <summary>
        /// 边界
        /// </summary>
        public Range Border_Forw { get { return borders[0].value; } }
        /// <summary>
        /// 边界
        /// </summary>
        public Range Border_Backw { get { return borders[1].value; } }

        /// <summary>
        /// 膜范围
        /// </summary>
        public RangeStruct Border { get; set; } = RangeStruct.InvalidValue;

        /// <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 DateTime UpdateTime { get; protected set; }

        /// <summary>
        /// 产品宽度，设置值 单位:脉冲
        /// 当膜超过范围（没有经过阀值），就使用它，猜边界在哪
        /// </summary>
        public int ProductWidth { get; protected set; }

        #endregion


        /// <summary>
        /// 边界
        /// </summary>
        protected class BorderWithOk
        {
            /// <summary>
            ///  边界值
            /// </summary>
            public Range value = new Range();
            /// <summary>
            /// 开始边界100%可靠
            /// </summary>
            public bool isBeginOk;
            /// <summary>
            /// 结束边界100%可靠
            /// </summary>
            public bool isEndOk;
            /// <summary>
            /// 真实可靠的边界值
            /// </summary>
            public Range real = new Range();

            public void UpdateReal(int sensorWidth, int expect, int mid, bool isBeginOk, bool isEndOk)
            {
                if (value.IsValid)
                {
                    real.Begin = value.Begin + sensorWidth / 2;
                    real.End = value.End - sensorWidth / 2;

                    //修正！！！
                    if (real.Width < expect)
                    {
                        if (isBeginOk && isEndOk)
                        {

                        }
                        else if (isBeginOk)
                        {
                            real.End = real.Begin + expect;
                        }
                        else if (isEndOk)
                        {
                            real.Begin = real.End - expect;
                        }
                        else //开始边界与结束边界都是假的
                        {
                            //居中处理
                            real.Begin = mid - expect / 2;
                            real.End = real.Begin + expect;
                        }
                    }
                }
            }

            public void UpdateReal(int sensorWidth, int expect, int mid)
            {
                UpdateReal(sensorWidth, expect, mid, isBeginOk, isEndOk);
            }
            public void UpdateReal(int sensorWidth)
            {
                if (value.IsValid)
                {
                    real.Begin = value.Begin + sensorWidth / 2;
                    real.End = value.End - sensorWidth / 2;
                }
            }
        }
        /// <summary>
        /// 正反向边界数据
        /// </summary>
        protected BorderWithOk[] borders = new BorderWithOk[2] { new BorderWithOk(), new BorderWithOk() };

        OBJComponents.Server.WarningSystem warningSystem;


        /// <summary>
        /// 用于调试， 进入到边界查找的数据
        /// </summary>
        protected BorderSearchGetViewReponse getViewReponse;

        private string param_path = "bordersearch.json";
        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(nameof(Border_Forw));
            };
            this.Border_Backw.PropertyChanged += (s, e) => {
                NotifyPropertyChanged(nameof(Border_Backw));
            };

            if (!Load()) {
                Save();
            }
        }

        public void Init(OBJComponents.Server.WarningSystem warningSystem) 
        {
            this.warningSystem = warningSystem;
        }
        void Default() 
        {
            Enable = false;
            Valid = new RangeStruct(1200,7400);
            TempAD = -1;
            N = 20;
            N2 = 400;
            N3 = 1000;
            SensorWidth = 250;
            TempRange = 500;
            TempRangePercent = 500.0 / 50000;
            IsTempRangeByPercent = true;

            Reset();
        }




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

        }

        public void Reset()
        {
            Border_Backw.Reset();
            Border_Forw.Reset();
            Width = 0;
            Border = RangeStruct.InvalidValue;
            Mid = Valid.Mid;
        }


        public void SetProductWidth(int productWidth) {
            ProductWidth = productWidth;
        }
        /// <summary>
        /// 获取扫描范围
        /// </summary>
        /// <returns></returns>
        public Range GetScanRange() 
        {
            GetScanRange(out int b, out int e);
            return new Range() { Begin = b, End = e };
        }
        /// <summary>
        /// 获取扫描范围
        /// </summary>
        /// <param name="posBegin"></param>
        /// <param name="posEnd"></param>
        public void GetScanRange(out int posBegin, out int posEnd)
        {

            int min, max;

            if (borders.All(b => b.value.IsValid))
            {
                min = borders.Min(b => b.value.Begin) - N3;
                max = borders.Max(b => b.value.End) + N3;
            }
            else if (borders[0].value.IsValid)
            {
                min = borders[0].value.Begin - N3;
                max = borders[0].value.End + N3;
            }
            else if (borders[1].value.IsValid)
            {
                min = borders[1].value.Begin - N3;
                max = borders[1].value.End + N3;
            }
            else 
            {
                //都不能找到边界
                min = Valid.Begin;
                max = Valid.End;
            }

            //范围限制
            posBegin = Math.Max(min, Valid.Begin);
            posEnd = Math.Min(max, Valid.End);
        }




        /// <summary>
        /// 初略操作，超过范围 就是边界
        /// </summary>
        /// <param name="dat">全部数据</param>
        /// <param name="validBegin_grid">有效范围开始</param>
        /// <param name="validEnd_grid">有效范围结束</param>
        /// <param name="borderBegin_grid">输出的开始边界</param>
        /// <param name="borderEnd_grid">输出的结束边界</param>
        /// <param name="isBorderBeginOk">开始边界经过阀值线</param>
        /// <param name="isBorderEndOk">结束边界经过阀值线</param>
        /// <returns></returns>
        protected bool FindFilm_Threshold(
            int[] dat, 
            int validBegin_grid,
            int validEnd_grid, 
            out int borderBegin_grid,
            out int borderEnd_grid, 
            out bool isBorderBeginOk, 
            out bool isBorderEndOk) 
        {
            borderBegin_grid = -1;
            borderEnd_grid = -1;
            isBorderBeginOk = false;
            isBorderEndOk = 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)
                            {
                                isBorderBeginOk = true;
                                //之前有膜
                                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;
                                isBorderBeginOk = true;
                                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)
                            {
                                isBorderEndOk = true;
                                //之前有膜
                                break;
                            }
                        }
                    }
                }
            }

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

            return true;
        }
        /// <summary>
        /// 精细操作，再向外找 折点
        /// </summary>
        /// <param name="dat"></param>
        /// <param name="sensor_grid">探头直径</param>
        /// <param name="filter_grid">滤波</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)
        {
            //把数据记录下来
            getViewReponse = new BorderSearchGetViewReponse()
            {
                direction = direction,
                posOfGrid = posOfGrid,
                gridBegin = gridBegin,
                dat = dat
            };
            UpdateTime = DateTime.Now;

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

            if (!FindFilm_Threshold(dat, 
                validbegin_grid, validend_grid, 
                out int borderbegin_grid, out int borderend_grid, 
                out bool isBorderBeginOk, out bool isBorderEndOk))
            {
                //warningSystem.Add(BORDER_ERRNO_NOFILM.Code, BORDER_ERRNO_NOFILM.Descrption, OBJComponents.Common.ERR_STATE.ON);
                return false;
            }
            else {
                //warningSystem.Add(BORDER_ERRNO_NOFILM.Code, BORDER_ERRNO_NOFILM.Descrption, OBJComponents.Common.ERR_STATE.OFF);
            }
            //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;
            var border = borders[idx];

            //转为 脉冲
            border.value.Begin = (borderbegin_grid + gridBegin) * posOfGrid;
            border.value.End = (borderend_grid +gridBegin) * posOfGrid;
            border.isBeginOk = isBorderBeginOk;
            border.isEndOk = isBorderEndOk;

            

            if (borders.All(b => b.value.IsValid))
            {
                foreach(var bo in borders)
                    bo.UpdateReal(SensorWidth, ProductWidth, Valid.Mid, 
                        borders.All(b=>b.isBeginOk), borders.All(b => b.isEndOk));
                Border = new RangeStruct(
                    (int)borders.Average(b => b.real.Begin),
                    (int)borders.Average(b => b.real.End));
            }
            else if (border.value.IsValid)
            {
                foreach (var bo in borders)
                    bo.UpdateReal(SensorWidth, ProductWidth, Valid.Mid);
                Border = border.real.ToStruct();
            }

            getViewReponse.border = border.real;

            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) 
        {
            int idx = (direction == DIRECTION.FORWARD) ? 0 : 1;
            var border = borders[idx];
            if (border.value.IsValid)
            {
                posBegin = border.real.Begin;
                posEnd = border.real.End;
            }
            else
            {
                idx += 1;
                if (idx > 1)
                    idx = 0;
                border = borders[idx];
                if (border.value.IsValid)
                {
                    posBegin = border.real.Begin;
                    posEnd = border.real.End;
                }
                else {
                    posBegin = Misc.MyBase.NULL_VALUE;
                    posEnd = Misc.MyBase.NULL_VALUE;
                }
            }
        }
        /// <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>
        /// 获取混合后的横向数据；没有把横向留白剔除；只把纵向留白剔除。
        /// 数据单位 mm
        /// </summary>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        [AsyncCb(typeof(BorderSearchGetViewReponse))]
        public void GetView(AsyncCBHandler asyncDelegate, object asyncContext) 
        {
            asyncDelegate?.Invoke(asyncContext, getViewReponse);
        }

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

        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

    }

    public class BorderSearchJsonDb
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<BorderSearch, BorderSearchJsonDb>().ReverseMap();
        }));
        public static bool Load(BorderSearch src, string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    string json = File.ReadAllText(filePath);
                    var p = JsonConvert.DeserializeObject<BorderSearchJsonDb>(json);
                    Mapper.Map(p, src);
                    return true;
                }
            }
            catch
            {
                //异常，没有json 解码失败
            }

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

            }
            return false;

        }

        public bool Enable;
        public bool IsBreakDetect = true;
        public RangeStruct Valid = new RangeStruct(200,7000);
        public bool TempADBySet;
        public int TempAD = 50000;
        public int TempRange = 500;
        public bool IsTempRangeByPercent  = true;
        public double TempRangePercent  = 0.02;
        public int N=20;
        public int SensorWidth=250;
        public int N2=400;
        public int N3=1000;
    }
}
