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

namespace FLY.Simulation.Coating
{

    public class Coating : INotifyPropertyChanged, Misc.ISaveToXml
    {
        public CoatingCtrl hmi_cc;
        public CoatingCtrl inner_cc;
        public HMI hmi;

        private FLY.Simulation.Coating.CoatingCtrl.COATINGMODE coatingmode = CoatingCtrl.COATINGMODE.Extrusion;
        /// <summary>
        /// 涂布类型
        /// </summary>
        public FLY.Simulation.Coating.CoatingCtrl.COATINGMODE CoatingMode 
        {
            get {
                return coatingmode;
            }
            set {
                if (coatingmode != value) 
                {
                    coatingmode = value;
                    hmi_cc.CoatingMode = value;
                    inner_cc.CoatingMode = value;
                }
            }
        }

        public int Port { get; set; } = 502;


        public class DataInfo
        {
            /// <summary>
            /// 上光纤是亮
            /// </summary>
            public bool IsLight;
            /// <summary>
            /// 下光纤是亮
            /// </summary>
            public bool IsLight2;
            /// <summary>
            /// 厚度值
            /// </summary>
            public double Thick;
        }
        
        //当前数据
        public List<DataInfo> Datas_Vertical = new List<DataInfo>();//纵向数据
        public List<DataInfo> Datas_Horizontal = new List<DataInfo>();//横向数据
        double datas_horizontal_avg = 0;
        int datas_vertical_position=0;
        /// <summary>
        /// 由于泵损坏带来的纵向波动
        /// </summary>
        double datas_vertical_pumpDamaged = 0;

        //30m 后的数据
        public List<DataInfo> Datas_Vertical_30m = new List<DataInfo>();//纵向数据
        public List<DataInfo> Datas_Horizontal_30m = new List<DataInfo>();//横向数据
        int datas_vertical_30m_position=0;



        /// <summary>
        /// 膜宽
        /// </summary>
        public int FilmWidth => Datas_Horizontal.Count();


        /// <summary>
        /// 膜速度 m/min
        /// </summary>
        public double FilmVelocity { get; set; } = 12;


        /// <summary>
        /// 辊周长 mm
        /// </summary>
        public double MmOfR=120*3.14;

        /// <summary>
        /// 辊信号长度 mm
        /// </summary>
        public double MmOfRSignal = 10;

        /// <summary>
        /// 当前辊位置 mm
        /// </summary>
        double RPosition = 0;


        /// <summary>
        /// 膜已经跑了的长度。m
        /// </summary>
        public double FilmLength { get; set; }

        /// <summary>
        /// 烘箱长度 30m
        /// </summary>
        public double OvenLength = 30;
        /// <summary>
        /// 辊信号 
        /// </summary>
        public bool RSignal;
        /// <summary>
        /// 上纵向信号
        /// </summary>
        public bool VSignal;
        /// <summary>
        /// 下纵向信号
        /// </summary>
        public bool VSignal2;
        /// <summary>
        /// 上横向信号
        /// </summary>
        public bool HSignal;
        /// <summary>
        /// 下横向信号
        /// </summary>
        public bool HSignal2;

        /// <summary>
        /// 设备状态
        /// </summary>
        public bool DeviceState { get; set; }

        /// <summary>
        /// 泵的转子坏了, 会看到有周期的波动,周期为 泵速
        /// </summary>
        public bool IsPumpDamaged { get; set; }

        /// <summary>
        /// 由于 泵的转子坏了 导致 面密度 周期的波动 的变化公差,单位g/m²
        /// </summary>
        public double ToleranceOfPumpBeDamaged { get; set; } = 1;

        /// <summary>
        /// 均值
        /// </summary>
        public double Avg { get; set; }

        void Load_Vertical()
        {
            Datas_Vertical.Clear();
            using (StreamReader sr = new StreamReader(@"battery\datas_vertical.csv"))
            {
                string header = sr.ReadLine();//标题 , 位置(mm), 纵向信号,厚度

                while (!sr.EndOfStream)
                {
                    string s = sr.ReadLine();
                    string[] ss = s.Split(',');

                    int pos_mm = int.Parse(ss[0]);
                    bool isLight = bool.Parse(ss[1]);
                    bool isLight2 = bool.Parse(ss[2]);
                    double thick = double.Parse(ss[3]);//单位 g/m²

                    Datas_Vertical.Add(new DataInfo() { IsLight = isLight, IsLight2 = isLight2, Thick = thick });
                    Datas_Vertical_30m.Add(new DataInfo() { IsLight = isLight, IsLight2 = isLight2, Thick = thick });
                }
                sr.Close();
            }
        }
        void Load_Horizontal()
        {
            Datas_Horizontal.Clear();
            using (StreamReader sr = new StreamReader(@"battery\datas_horizontal.csv"))
            {
                string header = sr.ReadLine();//标题 , 位置(mm), 厚度

                while (!sr.EndOfStream)
                {
                    string s = sr.ReadLine();
                    string[] ss = s.Split(',');

                    int pos_mm = int.Parse(ss[0]);
                    bool isLight = bool.Parse(ss[1]);
                    bool isLight2 = bool.Parse(ss[2]);
                    double thick = double.Parse(ss[3]);

                    Datas_Horizontal.Add(new DataInfo() { IsLight = isLight, IsLight2 = isLight2, Thick = thick });
                    Datas_Horizontal_30m.Add(new DataInfo() { IsLight = isLight, IsLight2 = isLight2, Thick = thick });
                }
                sr.Close();
            }
        }


        void Load() 
        {
            Load_Vertical();
            Load_Horizontal();

            datas_horizontal_avg = Datas_Horizontal.Average((d) => { return d.Thick; });
            Misc.SaveToXmlHepler.Load("simulation_coating.xml", this);
        }
        public void Save() 
        {
            Misc.SaveToXmlHepler.Save("simulation_coating.xml", this);
        }
        public Coating() 
        {
            hmi_cc = new CoatingCtrl(this);
            inner_cc = new CoatingCtrl(this);
            DeviceState = true;
            Load();
            hmi = new HMI(this, hmi_cc, Port);

            this.PropertyChanged += new PropertyChangedEventHandler(Coating_PropertyChanged);
        }

        void Coating_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(CoatingMode))
                Save();
        }

        /// <summary>
        /// 获取30m后的数据
        /// </summary>
        /// <param name="mm"></param>
        /// <returns></returns>
        public double GetData30m(int mm) 
        {
            if (mm < 0)
                return 0;
            if (mm >= FilmWidth)
                return 0;
            double d = Datas_Vertical_30m[datas_vertical_position].Thick * Datas_Horizontal_30m[mm].Thick / datas_horizontal_avg;
            if (IsPumpDamaged)
            {
                d += datas_vertical_pumpDamaged;
            }
            return d;
        }
        public double GetData(int mm) 
        {
            if (mm < 0)
                return 0;
            if (mm >= FilmWidth)
                return 0;

            double d = Datas_Vertical[datas_vertical_position].Thick * Datas_Horizontal[mm].Thick / datas_horizontal_avg;

            if (IsPumpDamaged)
            {
                d += datas_vertical_pumpDamaged;
            }
            return d;
        }
        /// <summary>
        /// 上一个时间点
        /// </summary>
        DateTime dtLast = DateTime.MinValue;
        
        public void OnPoll(DateTime now) 
        {
            if (dtLast == DateTime.MinValue)
            {
                dtLast = now;
                return;
            }
            TimeSpan ts= now - dtLast;
            dtLast = now;

            if (!DeviceState)//没有涂布
                return;

            double filmMove = ts.TotalMinutes * FilmVelocity;
            FilmLength += filmMove;//膜位置



            int p1 = (int)(FilmLength * 1000 % Datas_Vertical.Count());
            if (p1 != datas_vertical_position)
            {
                datas_vertical_position = p1;

                double avg = Datas_Vertical[datas_vertical_position].Thick;
                
                if (hmi_cc.NowCtrl.Pump > 0 && IsPumpDamaged)
                {
                    // 泵损坏 周期 m
                    double intervalOfPumpDamage = FilmVelocity / hmi_cc.NowCtrl.Pump;
                    double index = FilmLength % intervalOfPumpDamage;
                    double percent = index / intervalOfPumpDamage;
                    double v = ToleranceOfPumpBeDamaged * Math.Sin(Math.PI * 2 * percent);
                    
                    datas_vertical_pumpDamaged = v;
                    avg += datas_vertical_pumpDamaged;
                }
                Avg = avg;
                AvgChanged?.Invoke(this, new AvgChangedEventArgs() { filmLength = FilmLength, avg = Avg });
            }
            



            datas_vertical_30m_position = (int)((FilmLength - OvenLength) * 1000 % Datas_Vertical.Count());
            if (datas_vertical_30m_position < 0)
                datas_vertical_30m_position += Datas_Vertical.Count();


            //更新纵向信号
            VSignal = Datas_Vertical[datas_vertical_position].IsLight;
            VSignal2 = Datas_Vertical[datas_vertical_position].IsLight2;



            //更新辊信号
            RPosition += filmMove * 1000;
            RPosition = RPosition % MmOfR;
            if (RPosition < MmOfRSignal)
                RSignal = true;
            else
                RSignal = false;


            hmi_cc.OnPoll(filmMove);
            inner_cc.OnPoll(filmMove);
        }

        /// <summary>
        /// 每1mm更新一次
        /// </summary>
        public event AvgChangedEventHandler AvgChanged;
        public delegate void AvgChangedEventHandler(object sender, AvgChangedEventArgs args);
        public class AvgChangedEventArgs : EventArgs {
            public double filmLength;
            public double avg;
        }
        public event Action DatasChanged;
        public event Action Datas30mChanged;
        public void NotifyDatasChanged() 
        {
            if (DatasChanged != null) 
            {
                DatasChanged();
            }
        }
        public void NotifyDatas30mChanged()
        {
            if (Datas30mChanged != null)
            {
                Datas30mChanged();
            }
        }
        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        public string[] GetSavePropertyNames()
        {
            return new string[] { 
                nameof(Port),
                nameof(CoatingMode)
            };
        }
    }


    /// <summary>
    /// 涂布控制量
    /// </summary>
    public class CoatingCtrlElement:INotifyPropertyChanged
    {
        /// <summary>
        /// 控制的时间
        /// </summary>
        public DateTime Time = DateTime.Now;


        public double Pump { get; set; }

        public double LeftDis { get; set; }


        public double RightDis { get; set; }


        /// <summary>
        /// 膜移动的距离
        /// </summary>
        public double FilmMove=0;

        public event PropertyChangedEventHandler PropertyChanged;
    }


    public class CoatingCtrl : INotifyPropertyChanged
    {
        Coating mCoating;

        #region 参数
        public enum COATINGMODE
        {
            /// <summary>
            /// 挤压涂布
            /// </summary>
            Extrusion,
            /// <summary>
            /// 转移涂布
            /// </summary>
            Transfer
        }

        /// <summary>
        /// 涂布类型
        /// </summary>
        public COATINGMODE CoatingMode = COATINGMODE.Extrusion;

        /// <summary>
        /// 泵速因子  1 个 面密度 = N个泵速, 只有挤压涂布有效
        /// </summary>
        public double PumpFactor = 0.5;

        /// <summary>
        /// 左右刀因子 1个面密度 = N个左右刀
        /// </summary>
        public double LeftRightFactor = 1;

        /// <summary>
        /// 当前涂布控制量
        /// </summary>
        public CoatingCtrlElement NowCtrl;
        CoatingCtrlElement NowCtrl_30m;
        #endregion



        /// <summary>
        /// 控制量
        /// </summary>
        List<CoatingCtrlElement> mCtrlElement = new List<CoatingCtrlElement>();

        public CoatingCtrl(Coating coating) 
        {
            mCoating = coating;
            NowCtrl = new CoatingCtrlElement() { FilmMove = 0, Pump = 90, LeftDis = 200, RightDis = 210 };
            NowCtrl_30m = new CoatingCtrlElement() { FilmMove = 0, Pump = 90, LeftDis = 200, RightDis = 210 };
        }


        public void OnPoll(double filmMove)
        {
            for (int i = 0; i < mCtrlElement.Count(); i++)
            {
                if (mCtrlElement[i].FilmMove<mCoating.OvenLength)
                {
                    mCtrlElement[i].FilmMove += filmMove;
                }
            }

            for (int i = 0; i < mCtrlElement.Count(); i++)
            {
                if (mCtrlElement[i].FilmMove>=mCoating.OvenLength)
                {
                    CoatingAction(NowCtrl_30m, mCtrlElement[i], mCoating.Datas_Vertical_30m, mCoating.Datas_Horizontal_30m, mCoating.NotifyDatas30mChanged);
                    mCtrlElement.RemoveAt(i);
                    i--;
                }
                else
                    break;
            }
        }

        /// <summary>
        /// 控制量生效
        /// </summary>
        /// <param name="element"></param>
        /// <param name="datas_vertical"></param>
        /// <param name="datas_horizontal"></param>
        void CoatingAction(CoatingCtrlElement now, CoatingCtrlElement element, List<Coating.DataInfo> datas_vertical, List<Coating.DataInfo> datas_horizontal, Action NotifyDatasChanged)
        {
            if (CoatingMode == COATINGMODE.Extrusion)
            {
                double d_left = element.LeftDis - now.LeftDis;
                double d_right = element.RightDis - now.RightDis;
                double d = (d_right - d_left) / LeftRightFactor * 100;

                for (int i = 0; i < datas_horizontal.Count(); i++)
                {
                    datas_horizontal[i].Thick += (int)(d * i / (datas_horizontal.Count() - 1) - d / 2);
                }


                double d_pump = element.Pump - now.Pump;
                d = (d_pump / PumpFactor) * 100;
                for (int i = 0; i < datas_vertical.Count(); i++)
                {
                    FLY.Simulation.Coating.Coating.DataInfo vi = datas_vertical[i];
                    vi.Thick += (int)d;
                    datas_vertical[i] = vi;
                }
            }
            else
            {
                double d_left = element.LeftDis - now.LeftDis;
                double d_right = element.RightDis - now.RightDis;
                double d = (d_right - d_left) / LeftRightFactor;

                for (int i = 0; i < datas_horizontal.Count(); i++)
                {
                    datas_horizontal[i].Thick += d * i / (datas_horizontal.Count() - 1) - d / 2;
                }

                d = (d_left + d_right) / 2 / LeftRightFactor;
                for (int i = 0; i < datas_vertical.Count(); i++)
                {
                    FLY.Simulation.Coating.Coating.DataInfo vi = datas_vertical[i];
                    vi.Thick += d;
                    datas_vertical[i] = vi;
                }
            }

            now.Pump = element.Pump;
            now.LeftDis = element.LeftDis;
            now.RightDis = element.RightDis;
            now.Time = element.Time;
            NotifyDatasChanged();
        }


        public void SetCoatingCtrl(double pump, double left, double right)
        {
            CoatingCtrlElement element = new CoatingCtrlElement() { Pump = pump, LeftDis = left, RightDis = right };
            mCtrlElement.Add(element);
            CoatingAction(NowCtrl, element, mCoating.Datas_Vertical, mCoating.Datas_Horizontal, mCoating.NotifyDatasChanged);
            
        }


        public event PropertyChangedEventHandler PropertyChanged;


    }
}