using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using AutoMapper;
using Newtonsoft.Json;
using System.IO;

namespace FLY.Thick.Base.Server
{
    //1.剔除 偏离平均值 大于 设定值
    //2.权重 滤波
    public class Reject : IRejectService, INotifyPropertyChanged
    {

        public bool Enable { get; set; }

        public int Number { get; set; }
        public double ThresholdRatio { get; set; }



        public int Sigma { get; set; }

        public int Range1 { get; set; }

        public int Range2 { get; set; }

        public int Target { get; set; }

        private bool[] bfilterdats;//该数据是否需要剔除
        private int[] filterdats;//滤波后的数据
        private int[] rejectdats;//剔除后的数据
        private int[] sigmadats;//剔除用sigma数据
        public int dats_len;//上面三个的数据长度

        public int PosLen { get; set; } = 8900;


        public int PosOfGrid { get; set; } = 10;

        public int[] FilterDatas
        {
            get
            {
                return filterdats;
            }
        }
        public int[] RejectDatas
        {
            get
            {
                return rejectdats;
            }
        }
        public int[] SigmaDatas
        {
            get
            {
                return sigmadats;
            }
        }

        public void Clear()
        {
            for (int i = 0; i < dats_len; i++)
                filterdats[i] = Misc.MyBase.NULL_VALUE;
            for (int i = 0; i < dats_len; i++)
                rejectdats[i] = Misc.MyBase.NULL_VALUE;
            for (int i = 0; i < dats_len; i++)
                sigmadats[i] = Misc.MyBase.NULL_VALUE;
            for (int i = 0; i < dats_len; i++)
                bfilterdats[i] = false;
        }
        public void Test()
        {
            int[] buf = new int[dats_len];
            for (int i = 0; i < buf.Length; i++)
            {
                buf[i] = 6000;
            }
            for (int i = 200; i < 300; i++)
            {
                buf[i] += (i - 200);
            }

            DoFrameFilter(6100, 0, buf);
        }
        private string param_path = "reject.xml";
        public Reject()
        {
            SetDefault();

            this.PropertyChanged += new PropertyChangedEventHandler(Reject_PropertyChanged);
        }
        
        public Reject(string param_path) 
        {
            if (!string.IsNullOrEmpty(param_path))
                this.param_path = param_path;

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

        void SetDefault() 
        {
            Enable = false;
            Number = 1;
            ThresholdRatio = 0.93;

            Range1 = 10;
            Range2 = Range1;
            Sigma = 80;
            int len = PosLen / PosOfGrid;
            Resize(len);
        }
        void Resize(int size)
        {
            dats_len = size;
            filterdats = new int[dats_len];
            rejectdats = new int[dats_len];
            sigmadats = new int[dats_len];
            bfilterdats = new bool[dats_len];
            Clear();
        }
        void Reject_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            //if (GetSavePropertyNames().Contains(e.PropertyName))
            //{
            //    FObjBase.PollModule.Current.Poll_JustOnce(new FObjBase.PollModule.PollHandler(delegate()
            //    {
            //        Save();
            //    }), this, 2);
            //}
            if ((e.PropertyName == nameof(PosLen)) || (e.PropertyName == nameof(PosOfGrid)))
            {
                int len = PosLen / PosOfGrid;
                Resize(len);
            }
        }
        public bool Load()
        {
            return RejectJsonDb.Load(param_path, this);
        }
        public bool Save()
        {
            return RejectJsonDb.Save(param_path, this);

        }
        public void Apply() 
        {
            Save();
        }
        public void DoFrameFilter(int target, int start_grid, int[] frame)
        {
            //if (!Enable)
            //    return;
            Clear();

            int len = frame.Length;
            if ((start_grid + len) > dats_len)
            {
                len = dats_len - start_grid;
            }

            
            Array.Copy(frame, 0, rejectdats, start_grid, len);

            int range1 = Range1 / PosOfGrid;
            //滤波
            for (int i = start_grid; i < (len + start_grid); i++)
            {
                
                int b = i - range1;
                int e = i + range1;
                if (b < 0) b = 0;
                if (e >= dats_len) e = dats_len - 1;
                filterdats[i] = Misc.MyMath.Avg(rejectdats, b, e);
            }

            //sigma
            for (int i = start_grid; i < (len + start_grid); i++)
            {
                int b = i - Number;
                int e = i + Number;

                if (b < 0) b = 0;
                if (e >= dats_len) e = dats_len - 1;

                sigmadats[i] = Misc.MyMath.Sigma(filterdats, b, e);

            }

            int threshold = (int)(target * ThresholdRatio);


            //阀值剔除
            for (int i = start_grid; i < (len + start_grid); i++)
            {
                
                if (Misc.MyBase.ISVALIDATA(rejectdats[i]) && (rejectdats[i] < threshold))
                {
                    bfilterdats[i] = true;
                }
            }


            int sigma;

            sigma = Sigma;

            //sigma剔除
            for (int i = start_grid; i < (len + start_grid); i++)
            {
                
                if ((!Misc.MyBase.ISVALIDATA(sigmadats[i])) || (sigmadats[i] > sigma))
                {
                    bfilterdats[i] = true;
                }
            }

            int reject_b_idx= -1;
            int reject_e_idx = -1; 
            //半径内全部删除
            int range2 = Range2 / PosOfGrid;
            for (int i = start_grid; i < (start_grid+len); i++)
            {
                if (!bfilterdats[i])
                {
                    //之前有需要剔除的
                    if (reject_b_idx != -1)
                    {
                        int b = reject_b_idx - range2;
                        int e = reject_e_idx + range2;
                        if (b < 0) b = 0;
                        if (e >= dats_len) e = dats_len - 1;
                        for (int j = b; j <= e; j++)
                        {
                            rejectdats[j] = Misc.MyBase.NULL_VALUE;
                        }
                        reject_b_idx = -1;
                        i = e;
                    }
                }
                else
                {
                    if (reject_b_idx == -1)
                    {
                        reject_b_idx = i;
                    }
                    reject_e_idx = i;
                }
            }
            if (reject_b_idx != -1)
            {
                int b = reject_b_idx - range2;
                int e = reject_e_idx + range2;
                if (b < 0) b = 0;
                if (e >= dats_len) e = dats_len - 1;
                for (int j = b; j <= e; j++)
                {
                    rejectdats[j] = Misc.MyBase.NULL_VALUE;
                }
                reject_b_idx = -1;
            }


            if (Enable) 
            {
                Array.Copy(rejectdats, start_grid, frame, 0, len);
                //剩下的全部剔除
                for (int i = len; i < frame.Length; i++)
                {
                    frame[i] = Misc.MyBase.NULL_VALUE;
                }
            }

            Target = target;
            NotifyPropertyChanged("FilterDatas");
            NotifyPropertyChanged("RejectDatas");
            NotifyPropertyChanged("SigmaDatas");
        }

        int ToThick(double thk) {
            if (double.IsNaN(thk))
                return Misc.MyBase.NULL_VALUE;
            else
                return (int)(thk * 100);
        }
        public void DoFrameFilter(double _target, int start_grid, double[] _thks)
        {
            int[] frame = _thks.Select(d => ToThick(d)).ToArray();
            DoFrameFilter(ToThick(_target), start_grid, frame);
            for (int i = 0; i < frame.Count(); i++) {
                if (!Misc.MyBase.ISVALIDATA(frame[i]))
                {
                    _thks[i] = double.NaN;
                }
            }

        }

        #region INotifyPropertyChanged 成员
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

    }

    public class RejectJsonDb
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<Reject, RejectJsonDb>().ReverseMap();
        }));

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

            }
            return false;
        }
        public static bool Load(string file_path, Reject src)
        {
            try
            {
                if (File.Exists(file_path))
                {
                    string json = File.ReadAllText(file_path);
                    var p = JsonConvert.DeserializeObject<RejectJsonDb>(json);
                    Mapper.Map(p, src);
                }
            }
            catch
            {
                //异常，没有json 解码失败

            }
            return false;
        }



        public bool Enable { get; set; }

        public double ThresholdRatio { get; set; }

        public int Sigma { get; set; }

        public int Range1 { get; set; }

        public int Range2 { get; set; }

    }
}
