using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using FlyADBase;
using FObjBase;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace FLY.Thick.Base.Server
{
    /// <summary>
    /// 膜位置检测;
    /// 通过辊接近开关，或者编码器2;
    /// 可以用于2台测厚仪同步主轴位置
    /// </summary>
    public class FilmPositionDetect : IFilmPositionDetectService
    {
        const int MARKNO_SYNC_HIGH = 1;
        public event PropertyChangedEventHandler PropertyChanged;
        #region 状态
        /// <summary>
        /// 线速度 m/min
        /// </summary>
        public double FilmVelocity { get; private set; }

        /// <summary>
        /// 膜位置， m
        /// </summary>
        public double FilmPosition { get; private set; }

        /// <summary>
        /// 复位标识，从同步服务器发送过来的
        /// </summary>
        public int ResetMark { get; private set; }
        #endregion

        #region 参数
        /// <summary>
        /// 线速度阀值，低于阀值，速度直接变为0
        /// </summary>
        public double FilmVThreshold { get; set; } = 2;

        /// <summary>
        /// 线速度来源
        /// </summary>
        public FilmVSRC FilmVSrc { get; set; } = FilmVSRC.ROUND;

        /// <summary>
        /// 线速度:编码器2 mm/pulse
        /// </summary>
        public double Encoder2_mmpp { get; set; } = 0.1;

        /// <summary>
        /// 线速度:1圈多少mm
        /// </summary>
        public double MmOfR { get; set; } = 314;

        #endregion


        /// <summary>
        /// 纵向光纤传感器检测到 0到1 变化 事件 
        /// 或
        /// sysTime同步信号触发 0到1 变化 事件 
        /// </summary>
        public event EventHandler SysTimeFilmPosEvent;


        /// <summary>
        /// 
        /// </summary>
        double reset_filmPos;

        /// <summary>
        /// 复位时，对应的pos2脉冲
        /// </summary>
        int reset_pos2;

        IFlyADClientAdv flyAd;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="flyAd"></param>
        public void Init(IFlyADClientAdv flyAd)
        {
            this.flyAd = flyAd;


            //------------------------------------------------------------------------------------------------------------------------------------
            //线速度------------------------------------------------------------------------------------------------------------------------------
            this.flyAd.PropertyChanged += FlyAd_PropertyChanged_EN2;

            //------------------------------------------------------------------------------------------------------------------------------------
            //辊信号生成线速度-----------------------------------------------------------------------------------------------------------------------------
            InitRoundFilmVelocity();

            flyAd.IStatusChangedEvent += mFlyAD_IStatusChangedEvent;

            //复位
            Misc.BindingOperations.SetBinding(this.flyAd, nameof(this.flyAd.IsReady), () =>
            {
                //刚连接上，复位
                ResetMark = 0;
                Reset(0);
            });
        }


        private void FlyAd_PropertyChanged_EN2(object sender, PropertyChangedEventArgs e)
        {
            if (FilmVSrc != FilmVSRC.EN2)
                return;

            if (e.PropertyName == nameof(flyAd.Speed2))
            {
                double speed = flyAd.Speed2 * Encoder2_mmpp / 1000.0;// m/s
                speed *= 60;//m/min
                FilmVelocity = speed;
            }
            else if (e.PropertyName == nameof(flyAd.Position2))
            {
                FilmPosition = Pos2ToFilmPos(flyAd.Position2);
            }
        }

        class DateTimeUnit
        {
            public DateTime dt { get; set; }
            public int data;
        }
        List<DateTimeUnit> pos2Pool = new List<DateTimeUnit>();
        void AddPos2(DateTime time, int pos2)
        {
            pos2Pool.Add(new DateTimeUnit() { dt = time, data = pos2 });
            if (pos2Pool.Last().dt - pos2Pool.First().dt > TimeSpan.FromMinutes(1))
            {
                pos2Pool.RemoveAt(0);
            }
        }
        bool FindPos2(DateTime time, out int pos2)
        {
            pos2 = 0;
            var unit = pos2Pool.Find(u => u.dt == time);
            if (unit == null)
                return false;
            pos2 = unit.data;
            return true;
        }
        void mFlyAD_IStatusChangedEvent(object sender, IStatusChangedEventArgs e)
        {
            if (Misc.MyBase.CHECKBIT(e.IChanged, FlyADIODefine.Instance.InNo_VSign))
            {
                if (Misc.MyBase.CHECKBIT(e.IStatus, FlyADIODefine.Instance.InNo_VSign))
                {
                    if (FilmVSrc == FilmVSRC.EN2)//不是用编码器， 纵向光纤也不会安装
                    {
                        SysTimeFilmPosEvent?.Invoke(this, new SysTimeFilmPosEventArgs()
                        {
                            IsSysTimeSync = false,
                            SysTime = e.Time,
                            FilmPos = Pos2ToFilmPos(e.Position2)
                        });

                        AddPos2(e.Time, e.Position2);
                    }
                }
            }
            else if (Misc.MyBase.CHECKBIT(e.IChanged, FlyADIODefine.Instance.InNo_Sync))
            {
                if (Misc.MyBase.CHECKBIT(e.IStatus, FlyADIODefine.Instance.InNo_Sync))
                {
                    if (FilmVSrc == FilmVSRC.EN2)
                    {
                        AddPos2(e.Time, e.Position2);
                        FilmPosition = Pos2ToFilmPos(e.Position2);
                    }

                    SysTimeFilmPosEvent?.Invoke(this, new SysTimeFilmPosEventArgs()
                    {
                        IsSysTimeSync = true,
                        SysTime = e.Time,
                        FilmPos = FilmPosition
                    });
                }
            }
        }

        #region 辊信号生成 线速度
        DispatcherTimer round_t;
        DateTime dtRound;

        int RCnt = 0;
        double BaseFilmPosition = 0;
        void InitRoundFilmVelocity()
        {
            round_t = new DispatcherTimer();
            round_t.Interval = TimeSpan.FromSeconds(1);
            round_t.Tick += new EventHandler(round_t_Tick);
            flyAd.IStatusChangedEvent += mFlyAD_IStatusChangedEvent_round;
        }


        void mFlyAD_IStatusChangedEvent_round(object sender, IStatusChangedEventArgs e)
        {
            if (FilmVSrc != FilmVSRC.ROUND)
                return;

            if (!Misc.MyBase.CHECKBIT(e.IChanged, FlyADIODefine.Instance.InNo_Roll))
                return;
            if (Misc.MyBase.CHECKBIT(e.IStatus, FlyADIODefine.Instance.InNo_Roll))
                return;

            //1->0
            DateTime dt = e.Time;
            if (dt <= dtRound)
                return;
            if (dtRound != DateTime.MinValue)
            {
                double v = MmOfR / 1000.0 / (dt - dtRound).TotalMinutes;
                if (v < FilmVThreshold)//太慢了
                {
                    dt = DateTime.MinValue;
                    v = 0;
                }
                FilmVelocity = v;
                if (v > 0)
                {
                    RCnt++;
                    FilmPosition = RCnt * MmOfR / 1000.0 + BaseFilmPosition;
                }
            }

            dtRound = dt;

            if (dtRound != DateTime.MinValue)
            {
                if (!round_t.IsEnabled)
                    round_t.Start();
            }
        }

        void round_t_Tick(object sender, EventArgs e)
        {
            if (FilmVSrc != FilmVSRC.ROUND)
            {
                round_t.Stop();
                return;
            }

            if (dtRound == DateTime.MinValue)
            {
                round_t.Stop();
                return;
            }

            //1->0
            DateTime dt = flyAd.Now;
            if (dt <= dtRound)
                return;

            double v = MmOfR / 1000.0 / (dt - dtRound).TotalMinutes;

            if (v >= (FilmVelocity - 1))
                return;

            FilmVelocity = v;


            double p1 = RCnt * MmOfR / 1000.0;
            double p2 = (dt - dtRound).TotalMinutes * FilmVelocity;
            if (p2 > MmOfR / 1000.0)
                p2 = MmOfR / 1000.0;
            p1 += p2;
            if (FilmPosition < p1)
                FilmPosition = p1;


            if (FilmVelocity <= FilmVThreshold)
            {
                dtRound = DateTime.MinValue;
                FilmVelocity = 0;
                round_t.Stop();
                return;
            }
        }

        #endregion




        /// <summary>
        /// 让同步信号输出 1到0 1秒后， 0到1
        /// </summary>
        public void SyncSysTime()
        {
            flyAd.SetOutputBit(FlyADIODefine.Instance.OutNo_Sync, false);

            //1秒后，复位同步信号
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD,
                () => {
                    flyAd.SetOutputBit(FlyADIODefine.Instance.OutNo_Sync, true);
                },
                TimeSpan.FromSeconds(1), true, false, this, MARKNO_SYNC_HIGH, true);
        }


        /// <summary>
        /// 设置膜位置为某值
        /// </summary>
        /// <param name="filmPos">极片位置m</param>
        public void Reset(double filmPos)
        {
            if (FilmVSrc == FilmVSRC.EN2)
            {
                reset_pos2 = flyAd.Position2;
                reset_filmPos = filmPos;
                FilmPosition = Pos2ToFilmPos(flyAd.Position2);
            }
            else
            {
                RCnt = 0;
                BaseFilmPosition = filmPos;
                FilmPosition = filmPos;
            }
        }

        /// <summary>
        /// 设置膜位置为某值; 同步服务器调用的
        /// </summary>
        /// <param name="filmPos">极片位置m</param>
        /// <param name="sysTime">AD盒sysTime</param>
        /// <param name="resetMark">复位标识</param>
        public void Reset(double filmPos, DateTime sysTime, int resetMark)
        {
            ResetMark = resetMark;

            if (FilmVSrc == FilmVSRC.EN2)
            {
                reset_filmPos = filmPos;

                //找回 sysTime 时，对应的flyAd.Position2
                if (FindPos2(sysTime, out int pos2))
                {
                    reset_pos2 = pos2;
                }
                else
                {
                    reset_pos2 = flyAd.Position2;
                }
                FilmPosition = Pos2ToFilmPos(flyAd.Position2);
            }
            else
            {
                RCnt = 0;
                BaseFilmPosition = filmPos;
                FilmPosition = filmPos;
            }
            
        }


        double Pos2ToFilmPos(int pos2)
        {
            int distance_p = pos2 - reset_pos2;

            double distance_m = distance_p * Encoder2_mmpp / 1000.0;

            double filmPos = distance_m + reset_filmPos;

            if (Math.Abs(distance_p) > 1000000)//不让它太大，不然乘法会溢出
            {
                //当距离很远，需要复位一下
                reset_pos2 = pos2;
                reset_filmPos = filmPos;
            }
            return filmPos;
        }
    }

}
