using FLY.Thick.Base.Common;
using FLY.Thick.Base.Server;
using FLY.Thick.FilmCasting.Common;
using FLY.Thick.FilmCasting.IService;
using FLY.Thick.FilmCasting.Server.Model;
using FlyADBase;
using Misc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Threading;
using DynArea = FLY.Thick.FilmCasting.Common.DynArea;
using FlyADIODefine = FLY.Thick.FilmCasting.Common.FlyADIODefine;

namespace FLY.Thick.FilmCasting.Server
{
    public class GM_Scan : GM_Base
    {
        const int MARKNO_PROFILE_ADD = 87;
        const int MARKNO_PROFILE_CHANGED = 88;

        BoltMapFilmCasting boltmap;
        BorderSearch borderSearch;
        Reject reject;
        DynArea dynArea;

        AD2ThkHandler Ad2Thk;
        FilmPositionDetect2 filmPositionDetect;
        FilmCastingProfileParam profileParam;
        FilmCastingProfile profile;
        HistoryDb historyDb;
        BulkDb bulkDb;
        GSample gsample;
        InitParam initParam;

        ScanMotion scanMotion;
        OBJComponents.Server.WarningSystem2 warningSystem;
        AutoRollDown autoRollDown;

        public GM_Scan()
        {
            GMState = CTRL_STATE.SCAN;
            scanMotion = new ScanMotion();
        }

        public void Init(
            FlyAD7 flyAd,
            GSample gsample, InitParam initParam, OBJComponents.Server.WarningSystem2 warningSystem,
            BoltMapFilmCasting boltmap, BorderSearch bordersearch, Reject reject,
            DynArea dynarea, AD2ThkHandler ad2thk,
            FilmCastingProfileParam profileParam, FilmCastingProfile profile, FilmPositionDetect2 filmPositionDetect,
            HistoryDb historyDb, BulkDb bulkDb
            )
        {
            base.Init(flyAd);

            this.gsample = gsample;
            this.initParam = initParam;
            this.warningSystem = warningSystem;

            this.boltmap = boltmap;
            this.borderSearch = bordersearch;
            this.reject = reject;

            this.dynArea = dynarea;
            this.Ad2Thk = ad2thk;

            this.profileParam = profileParam;
            this.profile = profile;
            this.filmPositionDetect = filmPositionDetect;

            this.historyDb = historyDb;
            this.bulkDb = bulkDb;

            scanMotion.Init(flyAd, gsample, initParam, warningSystem);

            autoRollDown = new AutoRollDown();
            autoRollDown.Init(profile);

            bulkDb.FinishEvent += BulkDb_FinishEvent;

            flyAd.IStatusChangedEvent += FlyAd_IStatusChangedEvent;
        }


        private void FlyAd_IStatusChangedEvent(object sender, IStatusChangedEventArgs e)
        {
            if (Misc.MyBase.CHECKBIT(e.IChanged, FlyADIODefine.Instance.InNo_Finish)) 
            {
                if (Misc.MyBase.CHECKBIT(e.IStatus, FlyADIODefine.Instance.InNo_Finish))
                {
                    FinishProfile();
                }
            }
        }

        private void BulkDb_FinishEvent()
        {
            FinishProfile();
        }

        void DataEndEvent(object sender, MiniGridEventArgs e)
        {
            DIRECTION direction = e.direction;
            DateTime now = DateTime.Now;

            int posOfGrid = e.posOfGrid;
            int start_grid = e.grid_start;
            int start_pos = e.grid_start * e.posOfGrid;

            int[] dat = e.buf;
            bulkDb.SetTempFrame(now, now, 0, null);

            boltmap.InitBoltAD();

            //if (gsample.Enable) 必须有样品，之前已经检查过！！！
            if (!gsample.OK())
                return;//样品还没获取成功

            if (!dynArea.IsTest && borderSearch.Enable)
            {
                //找边界
                if (!borderSearch.FindFilm(direction, posOfGrid, start_grid, dat))//不能找到边界!
                {
                    borderSearch.Reset();
                    boltmap.InitExChange();
                }

                //设置扫描范围
                //POLLSCAN----------------------------------------	
                Range scanr = borderSearch.GetScanRange();//获取边界查找需要的扫描范围

                //更新扫描范围
                scanMotion.ScanRange = scanr.ToStruct();

                Range border = borderSearch.GetBorder(direction);//返回X光测量 膜的斜波中间位置

                boltmap.CalExChange(direction, border);

                if (!border.IsValid)
                {
                    return;//不能找到膜
                }
            }
            else {
                //设置 膜宽为全长
                dynArea.Width = scanMotion.ScanRange.Width * initParam.Encoder1_mmpp;
            }
            //----------------------------------------------------
            //FRAME
            int len = dat.Length;
            double[] _temp_thks = new double[len];//减少数据量, 降低噪声，避免剔除时，把噪声剔除。剔除的目前只是为了剔走基材，不是为了滤波


            //AD值转厚度值
            for (int i = 0; i < len; i++)
            {
                _temp_thks[i] = Ad2Thk(dat[i]);
            }

            //基材剔除
            reject.DoFrameFilter(profileParam.Target, posOfGrid, start_grid, _temp_thks);

            //分区 变为1cm一个数据
            double[] m_boltmap_thk = boltmap.Map(direction, start_pos, posOfGrid, _temp_thks);

            int dataBeginIdx = profileParam.DataRange.Begin;
            int dataEndIdx = profileParam.DataRange.End;
            if (profileParam.IsAutoDataRange) {
                //自动计算数据范围
                if (!dynArea.IsTest && borderSearch.Enable) 
                {
                    Range border = borderSearch.GetBorder(direction);//返回X光测量 膜的斜波中间位置
                    
                    if (border.IsValid) 
                    {
                        int cut_p = (int)(profileParam.SideCutWidth / initParam.Encoder1_mmpp);
                        
                        int beginPos = boltmap.PositionExchange(direction, border.Begin);
                        beginPos += cut_p;
                        dataBeginIdx = boltmap.GetBoltIndex_fromPos(beginPos);

                        int endPos = boltmap.PositionExchange(direction, border.End);
                        endPos -= cut_p;
                        dataEndIdx = boltmap.GetBoltIndex_fromPos(endPos);

                        
                        if (dataBeginIdx> dataEndIdx) 
                        {
                            int temp = dataEndIdx;
                            dataEndIdx = dataBeginIdx;
                            dataBeginIdx = temp;
                        }
                        profileParam.DataRange = new RangeStruct(dataBeginIdx, dataEndIdx);
                    }
                    
                }

            }
            
            //数据不剔除！！！！！！！！！！！！！！！！！！！！！！！
            //把数据范围外删除

            //for (int i = 0; i < dataBeginIdx; i++)
            //    m_boltmap_thk[i] = double.NaN;

            //for (int i = dataEndIdx; i < m_boltmap_thk.Length; i++)
            //    m_boltmap_thk[i] = double.NaN;

            double[] thicks = m_boltmap_thk;

            //当整幅都没有数据，不保存！！！
            if (thicks.All(d => double.IsNaN(d)))
                return;



            #region sqlite3 历史数据保存
            //----------------------------------------------------------------
            //sqlite3 历史数据保存
            //----------------------------------------------------------------
            for (int i = 0; i < thicks.Count(); i++) {
                if (!double.IsNaN(thicks[i]))
                    thicks[i] = Math.Round(thicks[i], 2);
            }

            var sampleAds = from sc in gsample.Samples where sc.Enable select sc.AD;

            //只计算范围内的均值
            double avg = Misc.MyMath.Avg(thicks, dataBeginIdx, dataEndIdx);

            avg = Math.Round(avg, 2);


            var scanData = new Lc_ScanData()
            {
                Time = scanMotion.StartDataEndTime,
                EndTime = now,
                Target = profileParam.Target,
                TolerancePercent = profileParam.TolerancePercent,
                K = profileParam.K,
                B = profileParam.B,

                FilmPosition = Math.Round(dynArea.FilmPosition, 2),
                FilmVelocity = Math.Round(dynArea.FilmVelocity, 1),
                FilmVelocityBeginStretching = Math.Round(dynArea.FilmVelocityBeforeStretching, 1),
                FilmWidth = (int)Math.Round(dynArea.Width, 0),
                Avg = avg,
                DataBegin = dataBeginIdx,
                DataEnd = dataEndIdx,
                Thicks = thicks,
                SampleADs = sampleAds.ToArray(),
                BoltMapInfo = new Lc_BoltMapInfo()
                {
                    BoltCnt = boltmap.BoltCnt,
                    BoltWidth = boltmap.BoltWidth,
                    LeftEmptyBolts = profileParam.LeftEmptyBolts,
                    RightEmptyBolts = profileParam.RightEmptyBolts,
                    UniformShrinkPercent = profileParam.UniformShrinkPercent,
                    MidOffset = profileParam.MidOffset
                }
            };

            //下辊检测
            if (historyDb.localDb.IsProfileFinished)
            {
                //已经下辊
                autoRollDown.Reset();
                AddProfile();
            }
            else
            {
                if (autoRollDown.CheckAtDataEndEvent(scanData)) {

                    //需要下辊
                    FinishProfile();
                    AddProfile();
                    scanData.FilmPosition = 0;
                }
            }

            historyDb.AddScanData(scanData);
            #endregion
        }
        void FinishProfile() {

            if (!historyDb.localDb.IsProfileFinished) 
            {
                historyDb.FinishProfile();
                if (int.TryParse(profile.Param.Number, out int number)) 
                {
                    number++;
                    profile.Param.Number = number.ToString();
                }
            }
                
            autoRollDown.Reset();
        }

        void DataPosEvent(object obj, MiniGridEventArgs e)
        {

            Misc.DIRECTION direction = e.direction;

            int posOfGrid = e.posOfGrid;
            int start_grid = e.grid_start;
            int start_pos = e.grid_start * e.posOfGrid;

            int[] data = e.buf;

            dynArea.AD = data[0];

            //2.转为thick
            double[] dataValue = data.Select(i => Ad2Thk(i)).ToArray();
            //3.动态分区
            if (boltmap.UpdateBoltValue(dataValue, start_pos, posOfGrid, direction, out int start_boltIndex, out double[] boltThick))
            {
                dynArea.Thk = boltThick[0];
                bulkDb.SetTempFrame(scanMotion.StartDataEndTime, DateTime.Now, start_boltIndex, boltThick);
            }
        }


        void StartCore()
        {
            if (IsRunning)
                return;

            base.Start();

            if (IsRunning == false)
            {
                warningSystem.Add(
                    ERRNOs.ERRNO_SCAN_FATAL.Code,
                    ERRNOs.ERRNO_SCAN_FATAL.Description, accessory: "AD盒连接断开");
                return;
            }
            //设置扫描范围
            int beginPos = 0, endPos = initParam.PosLength;

            if (!borderSearch.Enable && !dynArea.IsTest)
            {
                warningSystem.Add(
                    ERRNOs.ERRNO_SCAN_FATAL.Code,
                    ERRNOs.ERRNO_SCAN_FATAL.Description, accessory: "边界查找未启动");
                return;
            }


            borderSearch.Reset();
            borderSearch.GetScanRange(out beginPos, out endPos);


            if (beginPos < 0)
                beginPos = 0;

            if (endPos > initParam.PosLength)
                endPos = initParam.PosLength;


            boltmap.InitBoltAD();



            var now = DateTime.Now;
            //清空临时数据
            bulkDb.SetTempFrame(now, now, 0, null);

            #region sqlite3 历史数据保存
            //检测是否新开一个profile，或者继续上一次 profile
            CheckAddProfile();
            profileParam.PropertyChanged += MProfileParam_PropertyChanged;
            #endregion

            scanMotion.DataEndEvent += DataEndEvent;
            scanMotion.DataPosEvent += DataPosEvent;
            if (!scanMotion.Start(beginPos, endPos))
            {
                Stop();
                return;
            }
        }
        /// <summary>
        /// 正常
        /// </summary>
        public override void Start()
        {
            StartCore();
        }
        #region sqlite3 历史数据保存
        string[] profile_propertynames_add = new string[]{
                nameof(FilmCastingProfileParam.PName),
                nameof(FilmCastingProfileParam.Batch),
                nameof(FilmCastingProfileParam.Number)
            };
        string[] profile_propertynames_update = new string[]{
                nameof(FilmCastingProfileParam.Target),
                nameof(FilmCastingProfileParam.TolerancePercent),
                nameof(FilmCastingProfileParam.ScanRange)
            };
        void CheckAddProfile()
        {
            if (historyDb.localDb.IsProfileFinished)
            {
                AddProfile();
                return;
            }

            if (historyDb.localDb.CurrProfile == null)
            {
                //没生产过
                AddProfile();
                return;
            }

            Db_Profile db_Profile = historyDb.localDb.CurrProfile;
            //上一次还没结束
            if ((db_Profile.PName != profileParam.PName) ||
            (db_Profile.Batch != profileParam.Batch) ||
            (db_Profile.Number != profileParam.Number))
            {
                //任何一个不一致
                AddProfile();
                return;
            }

            if (autoRollDown.CheckAtStart())
            {
                //满足要求，下辊
                AddProfile();
                return;
            }

            //继续上一次生产！！！！
            UpdateProfile();
        }



        void AddProfile()
        {
            //复位 换profile 检查
            autoRollDown.Reset();

            //复位 膜纵向位置
            filmPositionDetect.Reset(0);

            historyDb.AddProfile(new Db_Profile()
            {
                PName = profileParam.PName,
                Batch = profileParam.Batch,
                Number = profileParam.Number,

                Target = profileParam.Target,
                TolerancePercent = profileParam.TolerancePercent,
                ScanBegin = profileParam.ScanRange.Begin,
                ScanEnd = profileParam.ScanRange.End,

            }, DateTime.Now);// scanMotion.StartDataEndTime);
        }
        void UpdateProfile()
        {
            historyDb.UpdateProfile(new Db_Profile()
            {
                PName = profileParam.PName,
                Batch = profileParam.Batch,
                Number = profileParam.Number,

                Target = profileParam.Target,
                TolerancePercent = profileParam.TolerancePercent,
                ScanBegin = profileParam.ScanRange.Begin,
                ScanEnd = profileParam.ScanRange.End
            });
        }
        private void MProfileParam_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (profile_propertynames_add.Contains(e.PropertyName))
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        AddProfile();//新
                    }, this, MARKNO_PROFILE_ADD);
            }
            else if (profile_propertynames_update.Contains(e.PropertyName))
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        UpdateProfile();//修改
                    }, this, MARKNO_PROFILE_CHANGED);
            }
        }
        #endregion

        public override void Stop()
        {
            scanMotion.DataEndEvent -= DataEndEvent;
            scanMotion.DataPosEvent -= DataPosEvent;

            scanMotion.Stop();
            #region sqlite3 历史数据保存
            profileParam.PropertyChanged -= MProfileParam_PropertyChanged;
            #endregion
            base.Stop();
        }
        protected override void OnPoll()
        {
            bool isError = false;
            if (scanMotion.OnPoll())
            {
                isError = true;
            }
            if (isError)
                Stop();
        }

    }

    /// <summary>
    /// 自动换卷
    /// </summary>
    class AutoRollDown
    {
        List<double> avgList = new List<double>();
        List<double> filmWidthList = new List<double>();
        int ListCap = 10;
        DateTime startTime;
        DateTime lastTime;
        FilmCastingProfile profile;
        public void Init(FilmCastingProfile profile) 
        {
            this.profile = profile;
        }
        public void Reset()
        {
            avgList.Clear();
            startTime = DateTime.MinValue;
            filmWidthList.Clear();
        }
        /// <summary>
        /// 返回 true，需要 换卷
        /// </summary>
        /// <param name="lc_ScanData"></param>
        /// <returns></returns>
        public bool CheckAtDataEndEvent(Lc_ScanData lc_ScanData)
        {
            //停止检测
            //if (lastTime != DateTime.MinValue)
            //{
            //    if (lc_ScanData.EndTime - lastTime > TimeSpan.FromMinutes(profile.RollDown_StopTimeMin))
            //    {
            //        return true;
            //    }
            //}
            lastTime = lc_ScanData.EndTime;

            //时间检测
            if (startTime != DateTime.MinValue)
            {
                if (lc_ScanData.EndTime - startTime > TimeSpan.FromHours(profile.RollDown_TimeoutHour))
                {
                    return true;
                }

                if (lc_ScanData.EndTime.Month != startTime.Month) {
                    //跨月必须换卷
                    return true;
                }
            }
            startTime = lc_ScanData.EndTime;

            //长度检测
            if (lc_ScanData.FilmPosition > profile.RollDown_FilmLength)
            {
                return true;
            }

            //均值检测
            double currAvg = lc_ScanData.Avg;
            if (avgList.Count() > 0)
            {
                double avg = avgList.Average();
                double d = Math.Abs(currAvg - avg);
                if (d > profile.RollDown_AvgChanged)
                {
                    return true;
                }
            }
            avgList.Add(currAvg);
            if (avgList.Count > ListCap)
                avgList.RemoveAt(0);

            //膜宽检测
            double currFilmWidth = lc_ScanData.FilmWidth;
            if (filmWidthList.Count() > 0)
            {
                double avg = filmWidthList.Average();
                double d = Math.Abs(currFilmWidth - avg);
                if (d > profile.RollDown_FilmWidthChanged)
                {
                    return true;
                }
            }
            filmWidthList.Add(currFilmWidth);
            if (filmWidthList.Count > ListCap)
                filmWidthList.RemoveAt(0);



            return false;
        }

        /// <summary>
        /// 返回 true，需要 换卷
        /// </summary>
        /// <returns></returns>
        public bool CheckAtStart() 
        {
            DateTime now = DateTime.Now;
            //时间检测
            if (startTime != DateTime.MinValue)
            {
                if (now - startTime > TimeSpan.FromHours(profile.RollDown_TimeoutHour))
                {
                    return true;
                }

                if (now.Month != startTime.Month)
                {
                    //跨月必须换卷
                    return true;
                }
            }

            //停止检测
            if (lastTime != DateTime.MinValue)
            {
                if (now - lastTime > TimeSpan.FromMinutes(profile.RollDown_StopTimeMin))
                {
                    return true;
                }
            }

            return false;
        }
    }
}
