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;

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;
        FilmPositionDetect 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, FilmPositionDetect 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);
        }


        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;//不能找到膜
                }
            }
            //----------------------------------------------------
            //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;

            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 = Math.Round(thicks.AverageNoNull(), 2);


            var scanData = new Lc_ScanData()
            {
                Time = scanMotion.StartDataEndTime,
                EndTime = now,
                Target = profileParam.Target,
                TolerancePercent = profileParam.TolerancePercent,
                K = profileParam.K,
                B = profileParam.B,
                FilmVelocity = Math.Round(dynArea.FilmVelocity, 1),
                FilmPosition = Math.Round(dynArea.FilmPosition, 2),
                FilmWidth = (int)Math.Round(dynArea.Width, 0),
                Avg = avg,
                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
                }
            };

            bool ret = autoRollDown.CheckAtDataEndEvent(scanData);
            if(ret)
                autoRollDown.Reset();

            //下辊检测
            if (historyDb.localDb.IsProfileFinished)
            {
                AddProfile();
            }
            else if(ret)
            {
                //需要下辊
                FinishProfile();
                AddProfile();
                scanData.FilmPosition = 0;
            }

            historyDb.AddScanData(scanData);
            #endregion
        }
        void FinishProfile() {
            historyDb.FinishProfile();
            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.Instance.SCAN_ERRNO_FATAL.Code,
                    ERRNOs.Instance.SCAN_ERRNO_FATAL.Descrption + " :AD卡连接断开");
                return;
            }
            //设置扫描范围
            int beginPos = 0, endPos = initParam.PosLength;

            if (!borderSearch.Enable && !dynArea.IsTest)
            {
                warningSystem.Add(
                    ERRNOs.Instance.SCAN_ERRNO_FATAL.Code,
                    ERRNOs.Instance.SCAN_ERRNO_FATAL.Descrption + " :边界查找未启动");
                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 历史数据保存
            CheckProfile();
            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 CheckProfile()
        {
            if (historyDb.localDb.IsProfileFinished)
            {
                //上一次已经结束


                //复位 膜纵向位置
                filmPositionDetect.Reset(0);
                autoRollDown.Reset();
            }
            else
            {
                Db_Profile db_Profile = historyDb.localDb.CurrProfile;
                //上一次还没结束
                if ((db_Profile.PName != profileParam.PName) ||
                (db_Profile.Batch != profileParam.Batch) ||
                (db_Profile.Number != profileParam.Number))
                {
                    //任何一个不一致
                    //结束上次的
                    FinishProfile();
                    //复位 膜纵向位置
                    filmPositionDetect.Reset(0);
                }
                else if (autoRollDown.CheckAtStart())
                {
                    //需要结束上次的
                    FinishProfile();

                    //复位 膜纵向位置
                    filmPositionDetect.Reset(0);
                }
                else 
                { 
                    //继续上一次生产！！！！
                    UpdateProfile();
                }
            }
        }



        void AddProfile()
        {
            //复位 膜纵向位置
            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,

            }, 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(
                    () =>
                    {
                        if (!historyDb.localDb.IsProfileFinished) 
                        {
                            FinishProfile();
                        }
                        
                    }, this, MARKNO_PROFILE_ADD);
            }
            else if (profile_propertynames_update.Contains(e.PropertyName))
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        if (!historyDb.localDb.IsProfileFinished)
                            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;
        }
    }
}
