using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using FObjBase;
using FlyADBase;
using FLY.Thick.Base.IService;
using System.IO;
using AutoMapper;
using Newtonsoft.Json;
using FObjBase.Reflect;

namespace FLY.Thick.Base.Server
{
    public class GM_ScanCorr : GM_Base, IScanCorrService
    {
        /// <summary>
        /// 速度
        /// </summary>
        public UInt32 Velocity { get; set; } = 1000;

        #region IScanCorrService 接口

        /// <summary>
        /// 使能！！
        /// </summary>
        public bool Enable { get; set; }

        /// <summary>
        /// 数据更新时间
        /// </summary>
        public DateTime[] UpdateTimes { get; private set; } = new DateTime[2];


        /// <summary>
        /// 当前操作的组
        /// </summary>
        public int CurrGroupIndex { get; private set; }

        /// <summary>
        /// 0~100
        /// </summary>
        public int Progress { get; private set; }

        /// <summary>
        /// 来回次数
        /// </summary>
        public int ScanCnt { get; set; } = 2;

        public int SmoothFactor { get; set; } = 30;
        #endregion
        
        

        string file_path;
        ScanMotion2 scanMotion;
        InitParam initParam;
        public ScanCorrGroup[] Groups;
        public GM_ScanCorr():this("scancorr.json")
        { 
            
        }

        public GM_ScanCorr(string param_path)
        {
            GMState = Common.CTRL_STATE.CORR;
            this.file_path = param_path;
            Load();

            //参数检测
            checkParam();

            this.PropertyChanged += GM_ScanCorr_PropertyChanged;
        }

        private void GM_ScanCorr_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Enable))
            {
                Save();
            }
        }

        void checkParam() {
            if (Groups == null || Groups.Count()!=2 || Groups[0]==null || Groups[1] == null) 
            {
                Groups = new ScanCorrGroup[2] { new ScanCorrGroup(), new ScanCorrGroup() };

            }
        }
        public void Init(FlyAD7 flyad, InitParam initParam)
        {
            base.Init(flyad);
            this.initParam = initParam;

            Misc.BindingOperations.SetBinding(initParam, nameof(initParam.VAccuracy), this, nameof(Velocity));


            scanMotion = new ScanMotion2();

            scanMotion.Init(flyad);
            scanMotion.EndEvent += GM_GageInfo_EndEvent;
            scanMotion.PropertyChanged += GM_GageInfo_PropertyChanged;

        }

        bool IsDataOK(ScanCorrGroup group)
        {
            if (group.OrgDatas == null 
                || group.OrgDatas.Count()!=2
                || group.OrgDatas[0]==null || group.OrgDatas[1]==null)
            {
                return false;
            }
            if ((group.PosLen != initParam.PosLength) || (group.PosOfGrid != initParam.PosOfGrid))
            {
                return false;
            }

            return true;
        }




        public void Start(int groupIndex, int scanCnt, int smoothFactor)
        {
            if (Groups == null || groupIndex < 0 || groupIndex >= Groups.Count())
            {
                return;
            }

            Enable = false;//关闭校正，让flyad7 输出的是原始数据
            SmoothFactor = smoothFactor;
            ScanCnt = scanCnt;
            CurrGroupIndex = groupIndex;

            Start();
        }
        public override void Start()
        {
            base.Start();
            if (!IsRunning)
                return;

            Progress = 0;
            var group = Groups[CurrGroupIndex];
            group.PosOfGrid = initParam.PosOfGrid;
            group.PosLen = initParam.PosLength;
            group.OrgDatas = null;
            group.CorrDatas = null;
            group.Avg = Misc.MyBase.NULL_VALUE;
            UpdateTimes[CurrGroupIndex] = DateTime.Now;
            NotifyPropertyChanged(nameof(UpdateTimes));

            scanMotion.Start(ScanCnt, Velocity);
        }

        public override void Stop()
        {
            var group = Groups[CurrGroupIndex];
            base.Stop();
            if (IsDataOK(Groups[CurrGroupIndex])){
                //顺便把CorrDatas也算出来
                //默认以10个数据，平均滤波
                group.CorrDatas = new int[2][];
                group.CorrDatas[0] = Smooth(group.OrgDatas[0], SmoothFactor);
                group.CorrDatas[1] = Smooth(group.OrgDatas[1], SmoothFactor);
                int avg0 = Misc.MyMath.Avg(group.CorrDatas[0]);
                int avg1 = Misc.MyMath.Avg(group.CorrDatas[1]);
                if (!Misc.MyBase.ISVALIDATA(avg0) || !Misc.MyBase.ISVALIDATA(avg1))
                {
                    group.CorrDatas = null;
                    group.Avg = Misc.MyBase.NULL_VALUE;
                    //失败

                }
                else {
                    group.Avg = (avg0 + avg1) / 2;
                }

                UpdateTimes[CurrGroupIndex] = DateTime.Now;
                NotifyPropertyChanged(nameof(UpdateTimes));
                Progress = 100;
            }
            else {
                //失败了
                group.PosOfGrid = initParam.PosOfGrid;
                group.PosLen = initParam.PosLength;
                group.OrgDatas = null;
                group.CorrDatas = null;
                group.Avg = Misc.MyBase.NULL_VALUE;
                UpdateTimes[CurrGroupIndex] = DateTime.Now;
                NotifyPropertyChanged(nameof(UpdateTimes));
            }
            Save();
        }

        int[] Smooth(int[] orgDatas, int smoothFactor = 20)
        {

            var filters = new int[orgDatas.Count()];
            for (int i = 0; i < orgDatas.Count(); i++)
            {
                int sum = 0;
                int cnt = 0;
                for (int j = 0; j < smoothFactor; j++)
                {
                    int index = i - smoothFactor / 2+j;
                    if (index < 0)
                        continue;
                    else if (index >= orgDatas.Count())
                        break;

                    if (Misc.MyBase.ISVALIDATA(orgDatas[index]))
                    {
                        sum += orgDatas[index];
                        cnt++;
                    }
                }
                if (cnt > 0)
                    filters[i] = sum / cnt;
                else
                    filters[i] = Misc.MyBase.NULL_VALUE;
            }
            return filters;
        }

        protected override void OnPoll()
        {
            if (scanMotion.OnPoll())
            {
                Stop();
            }
        }
        private void GM_GageInfo_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ScanMotion2.Progress))
            {
                Progress = scanMotion.Progress;
            }
        }

        /// <summary>
        /// 清空组数据
        /// </summary>
        /// <param name="groupIndex"></param>
        public void Clear(int groupIndex) {
            if (groupIndex < 0 || groupIndex >= Groups.Count())
                return;

            Groups[groupIndex].Clear();
            UpdateTimes[groupIndex] = DateTime.Now;
            NotifyPropertyChanged(nameof(UpdateTimes));
            Save();
            
        }
        private void GM_GageInfo_EndEvent(object obj, MiniGridEventArgs e)
        {
            DataEnd(e.direction, e.posOfGrid, e.grid_start, e.buf);
        }
        void DataEnd(Misc.DIRECTION direction, int posOfGrid, int start_grid, int[] dat)
        {
            //posOfGrid 肯定为 initParam.PosOfGrid
            //start_grid 肯定为 0
            var group = Groups[CurrGroupIndex];
            if (group.OrgDatas == null)
                group.OrgDatas = new int[2][];
            int orgDatas_index = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            if (group.OrgDatas[orgDatas_index] == null)
            {
                group.OrgDatas[orgDatas_index] = dat.ToArray();
            }
            else
            {
                var orgDatas = group.OrgDatas[orgDatas_index];

                for (int i = 0; i < orgDatas.Count(); i++)
                {
                    var org_ad = orgDatas[i];
                    var ad = dat[i];
                    if (Misc.MyBase.ISVALIDATA(org_ad) && Misc.MyBase.ISVALIDATA(ad))
                    {
                        org_ad = (org_ad * scanMotion.CurrCnt + ad) / (scanMotion.CurrCnt + 1);
                    }
                    else
                    {
                        org_ad = ad;
                    }
                    orgDatas[i] = org_ad;
                }
            }
            UpdateTimes[CurrGroupIndex] = DateTime.Now;
            NotifyPropertyChanged(nameof(UpdateTimes));
        }



        /// <summary>
        /// 设置修正曲线
        /// </summary>
        /// <param name="groupIndex">组序号</param>
        /// <param name="corrDatas">修正数据</param>
        /// <param name="avg">均值</param>
        /// <param name="orgDatas">原始数据</param>
        public void SetCorrData(int groupIndex, int[][] corrDatas, int avg, int[][] orgDatas) 
        {
            if (Groups == null || groupIndex < 0 || groupIndex >= Groups.Count())
            {
                return;
            }

            var group = Groups[groupIndex];
            group.CorrDatas = corrDatas;
            group.OrgDatas = orgDatas;
            group.Avg = avg;
            Save();
        }


        [Call(typeof(GetScanCorrGroupResponse))]
        public void GetScanCorrGroup(int groupIndex, AsyncCBHandler asyncDelegate, object asyncContext) 
        {
            if (Groups == null || groupIndex < 0 || groupIndex >= Groups.Count())
            {
                asyncDelegate(asyncContext, new GetScanCorrGroupResponse() { GroupIndex = groupIndex });
                return;
            }


            var group = Groups[groupIndex];
            var reponse = new GetScanCorrGroupResponse()
            {
                GroupIndex = groupIndex,
                IsDataOK = IsDataOK(group),
                Avg = group.Avg,
                OrgDatas = group.OrgDatas,
                CorrDatas = group.CorrDatas,
                UpdateTime = UpdateTimes[groupIndex]
            };

            asyncDelegate(asyncContext, reponse);
        }

        public void CorrectADs(Misc.DIRECTION direction, int start_grid, int[] dat)
        {
            if (Enable && initParam.HasScanCorr)
            {
                for (int i = 0; i < dat.Length; i++)
                {
                    int grid = start_grid + i;

                    dat[i] = CorrectAD(direction, grid, dat[i]);
                }
            }
        }
        
        public int CorrectAD(Misc.DIRECTION direction, int grid, int ad)
        {
            if ((Misc.MyBase.ISVALIDATA(ad) == false) ||
                (Misc.MyBase.ISVALIDATA(grid) == false) ||
                (Enable == false) ||
                (initParam.HasScanCorr == false))
                return ad;

            //没有修正数据
            if (Groups[0].CorrDatas == null && Groups[1].CorrDatas == null)
                return ad;
            
            int datasize;
            if (Groups[0].CorrDatas != null)
                datasize = Groups[0].CorrDatas[0].Count();
            else
                datasize = Groups[1].CorrDatas[0].Count();

            if (grid < 0)
                grid = 0;
            else if (grid >= datasize)
                grid = datasize - 1;

            int ad0 = Misc.MyBase.NULL_VALUE;//group 0
            int ad1 = Misc.MyBase.NULL_VALUE;//group 1
            int ad0_avg = Groups[0].Avg;//group 0
            int ad1_avg = Groups[1].Avg;//group 1

            if (direction == Misc.DIRECTION.FORWARD)
            {
                if (Groups[0].CorrDatas != null)
                    ad0 = Groups[0].CorrDatas[0][grid];

                if (Groups[1].CorrDatas != null)
                    ad1 = Groups[1].CorrDatas[0][grid];
            }
            else if (direction == Misc.DIRECTION.BACKWARD)
            {
                if (Groups[0].CorrDatas != null)
                    ad0 = Groups[0].CorrDatas[1][grid];

                if (Groups[1].CorrDatas != null)
                    ad1 = Groups[1].CorrDatas[1][grid];
            }
            else
            {

                int ad2 = Misc.MyBase.NULL_VALUE;
                int ad3 = Misc.MyBase.NULL_VALUE;
                if (Groups[0].CorrDatas!=null)
                {
                    ad0 = Groups[0].CorrDatas[0][grid];
                    ad2 = Groups[0].CorrDatas[1][grid];
                }
                if (Groups[1].CorrDatas != null)
                {
                    ad1 = Groups[1].CorrDatas[0][grid];
                    ad3 = Groups[1].CorrDatas[1][grid];
                }

                if (Misc.MyBase.ISVALIDATA(ad0) && Misc.MyBase.ISVALIDATA(ad2))
                    ad0 = (ad0 + ad2) / 2;
                else if (Misc.MyBase.ISVALIDATA(ad2))
                    ad0 = ad2;

                if (Misc.MyBase.ISVALIDATA(ad1) && Misc.MyBase.ISVALIDATA(ad3))
                    ad1 = (ad1 + ad3) / 2;
                else if (Misc.MyBase.ISVALIDATA(ad3))
                    ad1 = ad3;

            }

            //TODO
            double x, p0, p1;
            if (Misc.MyBase.ISVALIDATA(ad0) && Misc.MyBase.ISVALIDATA(ad1))
            {
                // ad0_avg 与 ad1_avg 偏差必须 40%以上
                double d = (double)Math.Abs(ad0_avg - ad1_avg) / (ad0_avg + ad1_avg);

                if (d < 0.2)
                {
                    //异常
                    x = (double)ad0_avg / ad0;

                    ad = (int)(x * ad);
                }
                else
                {
                    p0 = (double)ad0_avg / ad0;
                    p1 = (double)ad1_avg / ad1;

                    x = (p0 - p1) * (ad - ad1_avg) / (ad0_avg - ad1_avg) + p1;

                    ad = (int)(x * ad);
                }
            }
            else if (Misc.MyBase.ISVALIDATA(ad0))
            {
                x = (double)ad0_avg / ad0;

                ad = (int)(x * ad);
            }
            else if (Misc.MyBase.ISVALIDATA(ad1))
            {
                x = (double)ad1_avg / ad1;

                ad = (int)(x * ad);
            }


            return ad;
        }

        bool Load()
        {
            if (File.Exists(file_path))
            {
                return ScanCorrJsonDb.Load(file_path, this);
            }
            else
            {
                //兼容 V6版本的.csv 文件

                //当存在 scancorr.csv,  转为 file_path, 删除 scancorr.csv
                string filepath = "scancorr.csv";
                if (File.Exists(filepath))
                {
                    bool ret = LoadCSV(filepath);

                    //删除
                    File.Delete(filepath);

                    //保存
                    Save();
                    return ret;
                }

                return false;
            }   
        }

        bool Save()
        {
            return ScanCorrJsonDb.Save(file_path, this);
        }

        bool LoadCSV(string filepath)
        {
            

            Groups = new ScanCorrGroup[2] { new ScanCorrGroup(), new ScanCorrGroup() };

            List<int>[] ADs = new List<int>[4] { new List<int>(), new List<int>(), new List<int>(), new List<int>() }; 
            try
            {
                bool keyvalue_state = true;//键值对模式
                using (StreamReader sw = new StreamReader(filepath, Encoding.GetEncoding("GB2312")))
                {
                    while (!sw.EndOfStream)
                    {
                        string item;
                        item = sw.ReadLine();
                        string[] items = item.Split(',');
                        if (keyvalue_state)
                        {
                            if (items.Count() >= 2)
                            {
                                if (items[0].StartsWith("Enable"))
                                {
                                    bool b;
                                    if (bool.TryParse(items[1], out b))
                                        Enable = b;
                                    else
                                        return false;
                                }
                                else if (items[0].StartsWith("ScanTimer"))
                                {
                                    int i;
                                    if (int.TryParse(items[1], out i))
                                        ScanCnt = i;
                                    else
                                        return false;
                                }
                                else if (items[0].StartsWith("SmoothFactor"))
                                {
                                    //int i;
                                    //if (int.TryParse(items[1], out i))
                                    //    SmoothFactor = i;
                                    //else
                                    //    return false;
                                }
                                else if (items[0].StartsWith("PosOfGrid"))
                                {
                                    int i;
                                    if (int.TryParse(items[1], out i))
                                    {
                                        Groups[0].PosOfGrid = i;
                                        Groups[1].PosOfGrid = i;
                                    }
                                    else
                                        return false;
                                }
                                else if (items[0].StartsWith("PosLen"))
                                {
                                    int i;
                                    if (int.TryParse(items[1], out i))
                                    {
                                        Groups[0].PosLen = i;
                                        Groups[1].PosLen = i;
                                    }
                                    else
                                        return false;
                                }
                                else if (items[0].StartsWith("position"))
                                {
                                    keyvalue_state = false;//正文
                                }
                            }
                        }
                        else
                        {
                            //正文
                            if (items.Count() == 0)
                            {
                                break;
                            }

                            for (int i = 1; i < 5; i++)
                            {
                                if (i >= items.Count())
                                    break;
                                int ad;
                                if (string.IsNullOrEmpty(items[i]))
                                    ad = Misc.MyBase.NULL_VALUE;
                                else
                                {
                                    if (!int.TryParse(items[i], out ad))
                                        ad = Misc.MyBase.NULL_VALUE;
                                }

                                ADs[i - 1].Add(ad);

                                //switch (i)
                                //{
                                //    case 1:
                                //        Group0.ForwDatas.Add(ad);
                                //        break;
                                //    case 2:
                                //        Group0.BackwDatas.Add(ad);
                                //        break;
                                //    case 3:
                                //        Group1.ForwDatas.Add(ad);
                                //        break;
                                //    case 4:
                                //        Group1.BackwDatas.Add(ad);
                                //        break;
                                //}
                            }
                        }
                    }
                }

                if (ADs[0].Count() == 0)
                    return true;
                if (ADs[0].All(ad => ad == Misc.MyBase.NULL_VALUE))
                    return true;
                if (ADs[1].All(ad => ad == Misc.MyBase.NULL_VALUE))
                    return true;

                Groups[0].OrgDatas = new int[2][];
                Groups[0].CorrDatas = new int[2][];
                Groups[0].OrgDatas[0] = ADs[0].ToArray();
                Groups[0].OrgDatas[1] = ADs[1].ToArray();
                Groups[0].CorrDatas[0] = ADs[0].ToArray();
                Groups[0].CorrDatas[1] = ADs[1].ToArray();

                if (ADs[2].All(ad => ad == Misc.MyBase.NULL_VALUE))
                    return true;
                if (ADs[3].All(ad => ad == Misc.MyBase.NULL_VALUE))
                    return true;

                Groups[1].OrgDatas = new int[2][];
                Groups[1].CorrDatas = new int[2][];
                Groups[1].OrgDatas[0] = ADs[2].ToArray();
                Groups[1].OrgDatas[1] = ADs[3].ToArray();
                Groups[1].CorrDatas[0] = ADs[2].ToArray();
                Groups[1].CorrDatas[1] = ADs[3].ToArray();


                //没数据了
                return true;
            }
            catch
            {
                return false;
            }
            return true;
        }
    }

    public class ScanCorrGroup
    {
        /// <summary>
        /// 机架总长，脉冲
        /// </summary>
        public int PosLen;

        /// <summary>
        /// 1个grid = N个pos
        /// </summary>
        public int PosOfGrid;

        /// <summary>
        /// 原始 正方向机架AD值数据, grid数据, 0:正方向 ; 1:反方向
        /// </summary>
        public int[][] OrgDatas;

        /// <summary>
        /// 修正用数据 0:正方向机架AD值数据 ; 1:反方向
        /// </summary>
        public int[][] CorrDatas;

        /// <summary>
        /// CorrDatas 的均值
        /// </summary>
        public int Avg;

        public ScanCorrGroup() {
        }

        public bool IsDataOK()
        {
            if (OrgDatas == null
                || OrgDatas.Count() != 2
                || OrgDatas[0] == null || OrgDatas[1] == null)
            {
                return false;
            }

            return true;
        }
        public void Clear() {
            Avg = Misc.MyBase.NULL_VALUE;
            CorrDatas = null;
            OrgDatas = null;
        }
    }


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

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

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

            }
            return false;
        }

        public bool Enable;

        public int ScanCnt = 2;
        public int SmoothFactor = 30;

        public ScanCorrGroup[] Groups;
    }
}
