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

using Misc;

using System.IO;
using System.ComponentModel;

using FLY.Thick.Base.IService;
using FLY.Thick.Base.Common;
using System.Collections.ObjectModel;

namespace FLY.Thick.Base.Server
{
    public class CoatingSegmentSearch : ICoatingSegmentService, ISaveToXml
    {
        #region 延时推送 MARKNO
        const int MARKNO_PUSH_SAVE = 1;
        #endregion
        #region ISegmentService
        private bool enable = false;
        public bool Enable
        {
            get { return enable; }
            set
            {
                if (enable != value)
                {
                    enable = value;
                    NotifyPropertyChanged("Enable");
                }
            }
        }
        private bool ensave = false;
        public bool EnSave
        {
            get
            {
                return ensave;
            }
            set
            {
                if (ensave != value)
                {
                    ensave = value;
                    NotifyPropertyChanged("EnSave");
                }
            }
        }
        private int savecnt = 500;
        public int SaveCnt
        {
            get
            {
                return savecnt;
            }
            set
            {
                if (savecnt != value)
                {
                    savecnt = value;
                    NotifyPropertyChanged("SaveCnt");
                }
            }
        }
        private int recentCnt = 20;
        /// <summary>
        /// 列表的行数
        /// </summary>
        public int RecentCnt 
        {
            get { return recentCnt; }
            set {
                if (recentCnt != value)
                {
                    recentCnt = value;
                    NotifyPropertyChanged("RecentCnt");
                }
            }
        }
        private int inidxofvside = 12;
        public int InIdxOfVSide
        {
            get
            {
                return inidxofvside;
            }
            set
            {
                if (inidxofvside != value)
                {
                    inidxofvside = value;
                    NotifyPropertyChanged("InIdxOfVSide");
                }
            }
        }
        private int inidxofround = 11;
        public int InIdxOfRound
        {
            get
            {
                return inidxofround;
            }
            set
            {
                if (inidxofround != value)
                {
                    inidxofround = value;
                    NotifyPropertyChanged("InIdxOfRound");
                }
            }
        }
        private double mmpr;
        public double MmOfR
        {
            get
            {
                return mmpr;
            }
            set
            {
                if (mmpr != value)
                {
                    mmpr = value;
                    NotifyPropertyChanged("MmOfR");
                }
            }
        }
        private double warningtol_total;
        public double WarningTol_Total
        {
            get
            {
                return warningtol_total;
            }
            set
            {
                if (warningtol_total != value)
                {
                    warningtol_total = value;
                    NotifyPropertyChanged("WarningTol_Total");
                }
            }
        }
        private double warningtol_distance;
        public double WarningTol_Distance
        {
            get
            {
                return warningtol_distance;
            }
            set
            {
                if (warningtol_distance != value)
                {
                    warningtol_distance = value;
                    NotifyPropertyChanged("WarningTol_Distance");
                }
            }
        }

        ObservableCollection<FlyData_CoatingSegmentHistory> m_coatingPartList = new ObservableCollection<FlyData_CoatingSegmentHistory>();
        public ObservableCollection<FlyData_CoatingSegmentHistory> Recent
        {
            get { return m_coatingPartList; }
        }
        FlyData_CoatingSegmentHistory last=null;
        public FlyData_CoatingSegmentHistory Last 
        {
            get {
                return last;
            }
            set {
                last = value;
                NotifyPropertyChanged("Last");
            }
        }
        #endregion
        #region 工艺
        CoatingSegmentProfile profile = new CoatingSegmentProfile();
        public CoatingSegmentProfile Profile { get { return profile; } }
        #endregion
        #region 输出

        double velocity;

        public double Velocity
        {
            get { return velocity; }
            set
            {
                if (velocity != value)
                {
                    velocity = value;
                    NotifyPropertyChanged("Velocity");
                }
            }
        }
        double length;
        public double Length
        {
            get { return length; }
            set
            {
                if (length != value)
                {
                    length = value;
                    NotifyPropertyChanged("Length");
                }
            }
        }
        #endregion
        class IOSign
        {

            public DateTime time;
            public bool b;
            public bool bHasSetPos2 = false;
            public double mm_pos2;
            public IOSign(DateTime t, bool b)
            {
                time = t;
                this.b = b;
            }
        }
        RList<IOSign> m_vsideList;
        RList<IOSign> m_roundList;
        DateTime m_lasttv = DateTime.MinValue;
        /// <summary>
        /// 时间与pos2关系
        /// </summary>
        class Pos2TV
        {
            public DateTime time;
            public double mm_pos2;
            public Pos2TV(DateTime t, double mm_pos2)
            {
                time = t;
                this.mm_pos2 = mm_pos2;
            }
        }
        RList<Pos2TV> m_pos2tvList;
        public FLY.Thick.RemoteHistory.History<FlyData_CoatingSegmentHistory> mHistory;

        /// <summary>
        /// 下一次得到（总长，留白），应该检测 的产品的序号， -1，上一次失败
        /// </summary>
        int m_check_idx=-1;

        /// <summary>
        /// 当前（总长，留白）， 对应于产品的序号
        /// </summary>
        int mCurrCoatingPart_Idx=0;

        enum Errno
        {
            OK = 0,
            ErrTotal=1,
            ErrDistance=2,
            ErrCount=3
        }
        /// <summary>
        /// 当前（总长，留白），的错误
        /// </summary>
        Errno mCurrCoatingPartErrno = Errno.OK;

        private IWarningServiceSimple mWarning;
        private FlyADBase.IFlyAD mFlyAD;
        public CoatingSegmentSearch()
        {
            m_vsideList = new RList<IOSign>(200);
            m_roundList = new RList<IOSign>(200);
            m_pos2tvList = new RList<Pos2TV>(200);

            mHistory = new Thick.RemoteHistory.History<FlyData_CoatingSegmentHistory>("history_coatingsegment");
            Load();
        }

        void Profile_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ProductName") 
            {
                //文件名被修改，读取
                LoadProductParam(Profile.ProductName);
            }
        }

        void CoatingSegmentSearch_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Enable") 
            {
                if (Enable)
                {
                    mFlyAD.IStatusChangedEvent += flyad_IStatusChangedEvent;
                    FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, OnPoll, TimeSpan.FromSeconds(1));
                }
                else 
                {
                    mFlyAD.IStatusChangedEvent -= flyad_IStatusChangedEvent;
                    FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.REMOVE, OnPoll, TimeSpan.FromSeconds(1));
                }
            }
        }

        public void Init(FlyADBase.IFlyAD flyad, WarningSystem warning, DynArea dynarea, InitParam initparam)
        {
            Misc.BindingOperations.SetBinding(initparam, "MmOfR", this, "MmOfR");

            Misc.BindingOperations.SetBinding(dynarea, "ProductName", Profile, "ProductName");

            Misc.BindingOperations.SetBinding(this, "Velocity", dynarea, "FilmVelocity");
            
            mWarning = warning;
            mFlyAD = flyad;
            if(Enable)
            {
                flyad.IStatusChangedEvent += flyad_IStatusChangedEvent;
                FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, OnPoll, TimeSpan.FromSeconds(1));
            }
            this.PropertyChanged += new PropertyChangedEventHandler(CoatingSegmentSearch_PropertyChanged);
            Profile.PropertyChanged += new PropertyChangedEventHandler(Profile_PropertyChanged);
        }

        void flyad_IStatusChangedEvent(object sender, FlyADBase.IStatusChangedEventArgs e)
        {
            bool bnew = false;

            if (Misc.MyBase.CHECKBIT(e.IChanged, InIdxOfVSide - 1))//纵向边界传感器 input 序号
            {
                bool bVSideSign = false;
                if (!Misc.MyBase.CHECKBIT(e.IStatus, InIdxOfVSide - 1))
                    bVSideSign = true;

                //添加
                IOSign ios = new IOSign(e.Time, bVSideSign);
                m_vsideList.Add(ios);

                bnew = true;
            }

            if (Misc.MyBase.CHECKBIT(e.IChanged, InIdxOfRound - 1))//辊圈数传感器 input 序号
            {
                bool bRoundSign = false;
                if (!Misc.MyBase.CHECKBIT(e.IStatus, InIdxOfRound - 1))
                    bRoundSign = true;

                //添加
                IOSign ios = new IOSign(e.Time, bRoundSign);
                m_roundList.Add(ios);
                m_lasttv = e.Time;

                while (RoundToPos2Tv()) ;//辊圈数传感器信息 列表 -> (时间&mm_脉冲2) 列表
                bnew = true;
            }

            if (bnew)
            {
                VSideFill_mm_pos2();
                while (VSide2CoatingPart()) ;
            }
        }

        void OnPoll()
        {
            OnPoll_CheckVelocity();
        }
        /// <summary>
        /// 1秒触发一次，为了检查什么时候没信号
        /// </summary>
        void OnPoll_CheckVelocity()
        {
            if (Velocity == 0)
                return;


            //最后一次获取信号到现在的时间间隔
            TimeSpan ts = mFlyAD.Now - m_lasttv;
            double mm_pos2 = MmOfR;
            double velocity = mm_pos2 / 1000.0 / ts.TotalMinutes;

            if (velocity < 2)//小于2m/mim
            {
                //长时间没信号，复位
                Velocity = 0;
                m_pos2tvList.Clear();
                m_vsideList.Clear();//纵向边界传感器 信号 列表
                m_roundList.Clear();//辊圈数传感器 信号 列表
                m_lasttv = DateTime.MinValue;
                m_check_idx = -1;
                h = new FlyData_CoatingSegmentHistory();
            }
            else if (velocity < (Velocity / 2))
            {
                Velocity = velocity;
            }
        }



        //辊圈数传感器信息 列表 -> (时间&mm_脉冲2) 列表
        bool RoundToPos2Tv()
        {
            //辊圈数传感器 信号 列表 数量要大于2
            int cnt = m_roundList.Count();
            if (cnt < 2)
                return false;

            IOSign ios_round;
            IOSign ios_round_next;

            //一定要是 1..0...............
            ios_round = m_roundList[0];
            ios_round_next = m_roundList[1];

            if (ios_round.b != true)
            {
                //删除
                m_roundList.RemoveAt(0);
                return true;
            }
            TimeSpan ts = ios_round_next.time - ios_round.time;
            ts = TimeSpan.FromTicks(ts.Ticks / 2);
            DateTime dt = ios_round_next.time - ts;


            Pos2TV p2tv = new Pos2TV(dt, 0);


            if (m_pos2tvList.Count() > 0)
            {
                Pos2TV p2tv_last = m_pos2tvList.Last();
                p2tv.mm_pos2 = p2tv_last.mm_pos2 + MmOfR;
            }
            m_pos2tvList.Add(p2tv);

            m_roundList.RemoveAt(0);
            m_roundList.RemoveAt(0);

            //(时间&mm_脉冲2) 转为速度
            Pos2TvToVelocity();

            return true;
        }

        /// <summary>
        /// (时间&mm_脉冲2) 转为速度
        /// </summary>
        void Pos2TvToVelocity()
        {
            int cnt = m_pos2tvList.Count();
            if (cnt < 2)
                return;

            Pos2TV pos2tv = m_pos2tvList[cnt - 2];
            Pos2TV pos2tv_1 = m_pos2tvList[cnt - 1];

            double mm_pos2 = (pos2tv_1.mm_pos2 - pos2tv.mm_pos2);
            TimeSpan ts = pos2tv_1.time - pos2tv.time;

            Velocity = mm_pos2 / ts.TotalMinutes / 1000.0;
            Length = pos2tv_1.mm_pos2 / 1000;
        }

        /// <summary>
        ///  时间 -> mm_脉冲2,
        ///  &lt;0, 时间在mm_pos2前面，永远转不了
        ///  ==0, 时间在mm_pos2内，转换成功
        ///  &gt;0, 时间在mm_pos2后面，以后可以转换
        /// </summary>
        /// <param name="time"></param>
        /// <param name="out_mm_pos2"></param>
        /// <returns></returns>
        int tvTo_mm_pos2(DateTime time, out double out_mm_pos2)
        {
            out_mm_pos2 = 0;
            //从最后开始查找,找出tv 夹在哪两个之间
            int cnt = m_pos2tvList.Count();
            int i;
            bool hasFind = false;
            for (i = cnt - 1; i >= 0; i--)
            {
                Pos2TV pos2tv = m_pos2tvList[i];
                if (time >= pos2tv.time)
                {
                    hasFind = true;//已经找到，它在 i 与 i+1 之间
                    break;
                }
            }

            if (hasFind)
            {
                if (i == cnt - 1)
                {
                    //第0个就是，不行，等以后有数据，再转换
                    return 1;
                }
                Pos2TV pos2tv = m_pos2tvList[i];
                Pos2TV pos2tv_1 = m_pos2tvList[i + 1];
                TimeSpan ts = time - pos2tv.time;
                TimeSpan ts2 = pos2tv_1.time - pos2tv.time;


                long usec2 = ts2.Ticks;
                long usec = ts.Ticks;

                double mm_pos2 = (pos2tv_1.mm_pos2 - pos2tv.mm_pos2) * usec / usec2 + pos2tv.mm_pos2;
                out_mm_pos2 = mm_pos2;
                return 0;
            }
            return -1;
        }

        /// <summary>
        /// 纵向边界信号 填充 mm_脉冲2信息
        /// </summary>
        void VSideFill_mm_pos2()
        {
            int cnt = m_vsideList.Count();
            int i;
            //当上一个没法从时间转为mm_pos2, 但这个可以，把上一个删除掉
            for (i = 0; i < cnt; i++)
            {
                IOSign ios_vside = m_vsideList[i];

                if (!ios_vside.bHasSetPos2)//还没翻译
                {
                    double mm_pos2;
                    int ret = tvTo_mm_pos2(ios_vside.time, out mm_pos2);

                    if (ret < 0)//边界信号的时间,在mm_pos2队列的前面,把这个和前面的边界信号全部删除 
                    {
                        //删除
                        int j;
                        for (j = 0; j <= i; j++)
                            m_vsideList.RemoveAt(0);
                        cnt -= (i + 1);
                        i = -1;
                    }
                    else if (ret == 0)//翻译成功
                    {
                        ios_vside.mm_pos2 = mm_pos2;
                        ios_vside.bHasSetPos2 = true;
                    }
                    else//边界信号的时间,在mm_pos2队列的未来,停止翻译
                    {
                        break;
                    }
                }
            }

        }

        //纵向边界信号 -> 涂布信息，涂长，留白，总长
        bool VSide2CoatingPart()
        {
            int cnt = m_vsideList.Count();
            if (cnt < 3)
                return false;

            //1为留白，0为涂料
            //一块涂布为		0000000000000000011111
            //对应信号列表为	0----------------1----0

            IOSign ios_vside0 = m_vsideList[0];
            IOSign ios_vside1 = m_vsideList[1];
            IOSign ios_vside2 = m_vsideList[2];


            if (!ios_vside0.bHasSetPos2)
                return false;
            if (!ios_vside1.bHasSetPos2)
                return false;
            if (!ios_vside2.bHasSetPos2)
                return false;

            //LogMessage2("segment.c",1,"SEGMENT_VSide2CoatingPart (%d,%d,%d)",ios_vside0->b,ios_vside1->b,ios_vside2->b);

            if (ios_vside0.b == true)
            {
                //删除，没用
                m_vsideList.RemoveAt(0);
                cnt = m_vsideList.Count();
                if (cnt < 3)
                    return false;
                return true;
            }


            //FlyData_CoatingSegmentHistory h = new FlyData_CoatingSegmentHistory();
            //h.time = ios_vside0.time;
            //ToSaveList(h);

            CoatingSegmentPart coatingpart = new CoatingSegmentPart();
            coatingpart.Total = ios_vside2.mm_pos2 - ios_vside0.mm_pos2;//涂长+留白单位mm
            coatingpart.Distance = ios_vside2.mm_pos2 - ios_vside1.mm_pos2;//留白单位mm
            
            
            CheckWarning(coatingpart);
            ToSaveList(coatingpart);



            //ToSaveList(coatingpart);
            //删除前两个！！！！！
            m_vsideList.RemoveAt(0);
            m_vsideList.RemoveAt(0);

            cnt = m_vsideList.Count();
            if (cnt < 3)
                return false;
            return true;
        }

        //下辊
        void RollDown()
        {
            mHistory.Flush();
            Recent.Clear();
        }
        FlyData_CoatingSegmentHistory h = new FlyData_CoatingSegmentHistory();

        void ToSaveList(CoatingSegmentPart coatingpart) 
        {
            int cnt = Profile.CoatingPartList.Count();
            if (cnt == 0)
                cnt = 1;
            h.PartCount = cnt;
            while (h.CoatingPart.Count < mCurrCoatingPart_Idx) 
            {
                h.CoatingPart.Add(new CoatingSegmentPart());
            }
            h.CoatingPart.Add(coatingpart);
            h.Errno = (int)mCurrCoatingPartErrno;
            h.Time = mFlyAD.Now;
            if (h.CoatingPart.Count == h.PartCount) 
            {
                //完成
                ToSaveList(h);
                h = new FlyData_CoatingSegmentHistory();
            }
            else if (mCurrCoatingPartErrno != Errno.OK) //报警
            {
                //完成
                ToSaveList(h);
                h = new FlyData_CoatingSegmentHistory();
            }
        }

        //压入数据列
        void ToSaveList(FlyData_CoatingSegmentHistory coatingpart)
        {
            Last = coatingpart;
            
            Recent.Add(coatingpart);
            
            if (Recent.Count > RecentCnt)
                Recent.RemoveAt(0);

            if (EnSave)
                mHistory.Add(coatingpart);
        }

        //异常检测，触发报警
        void CheckWarning(CoatingSegmentPart coatingpart)
        {
            if (IsWarning(coatingpart, ref m_check_idx))
            {
                //报警！！！！！！！！
                mWarning.Add(
                    ERRNOs.SEGMENT_ERRNO_OVERRUN.Type, 
                    ERRNOs.SEGMENT_ERRNO_OVERRUN.Code,
                    ERRNOs.SEGMENT_ERRNO_OVERRUN.Descrption,
                    ERR_STATE.ONCE);
            }
        }
        int FindSame(CoatingSegmentPart coatingpart)
        {
            int cnt = Profile.CoatingPartList.Count();
            //找最接近的
            int i;
            double min = 99999;
            int idx = -1;
            for (i = 0; i < cnt; i++)
            {
                CoatingSegmentPart cp = Profile.CoatingPartList[i];
                double d = Math.Abs(cp.Total - coatingpart.Total);
                if ((idx == -1) || (min > d))
                {
                    min = d;
                    idx = i;
                }
            }
            return idx;
        }
        bool IsWarning_1(CoatingSegmentPart coatingpart, int check_idx)
        {
            int cnt = Profile.CoatingPartList.Count();
            CoatingSegmentPart cp = Profile.CoatingPartList[check_idx];
            double[] d = new double[3];
            d[0] = Math.Abs(cp.Distance - coatingpart.Distance);
            //d[1] = cp->mm_segment-coatingpart->mm_segment;
            d[2] = Math.Abs(cp.Total - coatingpart.Total);
            if (d[2] > WarningTol_Total)
            {
                mCurrCoatingPartErrno = Errno.ErrTotal;
                return true;
            }
            if (d[0] > WarningTol_Distance)
            {
                mCurrCoatingPartErrno = Errno.ErrDistance;
                return true;
            }
            mCurrCoatingPartErrno = Errno.OK;
            return false;
        }

        //该段涂布，是否异常！！！
        bool IsWarning(CoatingSegmentPart coatingpart, ref int check_idx)
        {
            int cnt = Profile.CoatingPartList.Count();
            if (cnt == 0)
            {
                mCurrCoatingPart_Idx = 0;
                mCurrCoatingPartErrno = Errno.OK;
                check_idx = -1;
                
                return false;
            }
            int ci = check_idx;
            if ((ci < 0) || (ci >= cnt))//上次匹配失败！
            {
                //找最接近的
                ci = FindSame(coatingpart);
                mCurrCoatingPart_Idx = ci;
                if (ci != 0)
                {
                    mCurrCoatingPartErrno = Errno.ErrCount;
                }
            }
            //检测 ci 是否一致
            bool isWarning = IsWarning_1(coatingpart, ci);

            if (isWarning)//不一致，报警！！！！
            {
                check_idx = -1;
                return true;
            }
            else //一致，检测下一个
            {
                ci++;
                if (ci >= cnt)
                    ci = 0;
                check_idx = ci;//更新下一个要检测的！！
                return false;
            }
        }
        void Save()
        {
            Misc.SaveToXmlHepler.Save("segment.xml", this);
        }
        bool Load()
        {
            return Misc.SaveToXmlHepler.Load("segment.xml", this);
        }

        bool isloading=false;
        void LoadProductParam(string productname)
        {
            isloading = true;
            mHistory.FileNameCustom = productname;
            string path = @"CoatingSegment\" + productname + ".xml";
            //所有产品段长信息，保存在 Gage1/CoatingWidth/..... 里面
            if (File.Exists(path))
                Misc.SaveToXmlHepler.Load(path, Profile);
            isloading = false;
        }

        bool SaveProductParam()
        {
            if (isloading)
                return false;
            //所有产品涂宽信息，保存在 Gage1/coatingwidth/..... 里面
            if (!Directory.Exists("CoatingSegment"))
                Directory.CreateDirectory("CoatingSegment");
            string path = @"CoatingSegment\" + Profile.ProductName + ".xml";
            return Misc.SaveToXmlHepler.Save(path, Profile);
        }
        public void ProfileApply() 
        {
            SaveProductParam();
        }
        public void Apply() 
        {
            Save();
        }
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        public string[] GetSavePropertyNames()
        {
            return new string[] {
                "Enable",
                "EnSave",
                "SaveCnt",
                "RecentCnt",
                "InIdxOfVSide",
                "WarningTol_Total",
                "WarningTol_Distance"
            };
        }
    }
}
