using FLY.OBJComponents.IService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using FLY.Modbus;

namespace FLY.OBJComponents.Server
{
    /// <summary>
    /// 属性更新计划
    /// </summary>
    public class PLCProxySystem : IPLCProxySystemService, IPropertyOpt
    {
        class Plan
        {
            /// <summary>
            /// 唯一ID
            /// </summary>
            public long ID;
            /// <summary>
            /// 上一次喂的时间
            /// </summary>
            public DateTime FeedTime;
            /// <summary>
            /// plc
            /// </summary>
            public List<ModbusMapper> mappers = new List<ModbusMapper>();
        }
        List<Plan> planIDs = new List<Plan>();
        public List<ModbusMapper.DataToRegs> DRMap = new List<ModbusMapper.DataToRegs>();
        public List<ModbusMapper_Client> PLCs = new List<ModbusMapper_Client>();
        public Dictionary<string, INotifyPropertyChanged> ObjNames { get; } = new Dictionary<string, INotifyPropertyChanged>();

        /// <summary>
        /// 与PLC连接成功
        /// </summary>
        public bool IsConnectedWithPLC { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public PLCProxySystem()
        {


        }
        public void Init()
        {
            foreach (ModbusMapper_Client plc in PLCs)
            {
                plc.NameDataChanged += plc_NameDataChanged;
            }

            
            Misc.BindingOperations.SetBinding(PLCs[0].mclient, "IsConnected", () =>
            {
                IsConnectedWithPLC = PLCs[0].mclient.IsConnected;

                if (!IsConnectedWithPLC)
                    ClearPlan();
            });

            foreach (var obj in ObjNames.Values)
            {
                obj.PropertyChanged += Obj_PropertyChanged;
            }


            FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD,
                OnPoll_plans, TimeSpan.FromSeconds(1));
        }

        private void Obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (isShield)
                return;
            List<ModbusMapper.DataToRegs> drs = DRMap;

            ModbusMapper.DataToRegs dr = drs.Find((_dr) => { return (_dr.propertyName == e.PropertyName) && (_dr.owner == sender); });
            if (dr == null)
                return;//不会刷新PLC

            //向HMI写入数据，mRegs每次只能写入一个，它有列表保存功能。
            //SetNameData可以执行很多次，不过它只能一个个发。
            dr.mapper.SetNameData(dr, Misc.PropertiesManager.GetValue(sender, e.PropertyName));
        }

        bool isShield = false;



        void plc_NameDataChanged(object sender, NameDataChangedEventArgs e)
        {
            ModbusMapper_Client plc = sender as ModbusMapper_Client;

            //这是从HMI 更新过来的，不需要向HMI写入
            isShield = true;
            Misc.PropertiesManager.SetValue(e.Owener, e.PropertyName, plc.GetNameData(e.Owener, e.PropertyName));
            isShield = false;
        }

        /// <summary>
        /// 设置更新计划
        /// </summary>
        /// <param name="objname">对象名称</param>
        /// <param name="propertynames"></param>
        /// <param name="planID">计划的编号，应该全局唯一,建议使用时间ticks</param>
        public void SetPlan(string objname, IEnumerable<string> propertynames, long planID)
        {
            Plan plan = planIDs.Find(p => p.ID == planID);
            if (plan == null)
            {
                plan = new Plan() { ID = planID, FeedTime = DateTime.Now };
                planIDs.Add(plan);
            }
            else
            {
                plan.FeedTime = DateTime.Now;
            }




            List<ModbusMapper.DataToRegs> drs = DRMap;
            foreach (string propertyname in propertynames)
            {
                ModbusMapper.DataToRegs dr = drs.Find((_dr) => { return (_dr.propertyName == propertyname && _dr.owner == ObjNames[objname]); });
                if (dr == null)
                    continue;
                dr.mapper.PlanAdd(planID, dr);
                if (!plan.mappers.Contains(dr.mapper))
                    plan.mappers.Add(dr.mapper);
            }
            foreach (ModbusMapper mapper in plan.mappers)
            {
                mapper.PlanMake();
            }

        }

        /// <summary>
        /// 更新计划持续，如果不喂狗，20s后停止更新数据
        /// </summary>
        /// <param name="planID">计划的编号</param>
        public void FeedPlan(long planID)
        {
            Plan plan = planIDs.Find(p => p.ID == planID);
            if (plan != null)
            {
                plan.FeedTime = DateTime.Now;
            }
        }

        /// <summary>
        /// 清除全部计划
        /// </summary>
        void ClearPlan()
        {
            if (planIDs.Count() > 0)
            {
                foreach (var plc in PLCs)
                {
                    plc.PlanClear();
                    plc.PlanMake();
                }
                planIDs.Clear();
            }
        }
        /// <summary>
        /// 1s 一次，看出哪个令牌过期
        /// </summary>
        void OnPoll_plans()
        {
            if (planIDs.Count() > 0)
            {
                var _remove =
                    from plan in planIDs
                    where (plan.ID != 0) && (DateTime.Now - plan.FeedTime) > TimeSpan.FromSeconds(10)
                    select plan;
                
                if (_remove.Count() > 0)
                {
                    List<Plan> remove = new List<Plan>(_remove);

                    List<ModbusMapper> mappers = new List<ModbusMapper>();
                    foreach (var plan in remove)
                    {
                        planIDs.Remove(plan);
                        foreach (var mapper in plan.mappers)
                        {
                            mapper.PlanRemove(plan.ID);
                            if (!mappers.Contains(mapper))
                                mappers.Add(mapper);
                        }
                    }
                    foreach (var mapper in mappers)
                    {
                        mapper.PlanMake();
                    }
                }
            }
        }

        public string[] GetSyncPropNames()
        {
            return new string[] { "IsConnectedWithPLC" };
        }

        public string[] GetNoSyncPropNames()
        {
            return null;
        }
    }
}
