using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.Util;
using Aitex.Core.WCF;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Event;
using MECF.Framework.Common.FAServices;
using MECF.Framework.Common.Gem;

namespace Aitex.Core.RT.Event
{
    public class EventManager : ICommonEvent
    {
        #region Variable

        private const string INFORMATION_EVENT = "INFORMATION_EVENT";

        private const string WARNING_EVENT = "WARNING_EVENT";

        private const string ALARM_EVENT = "ALARM_EVENT";

        public event Action<EventItem> FireEvent;

        public event Action<EventItem> OnAlarmEvent;

        public event Action<EventItem> OnEvent;

        private FixSizeQueue<EventItem> _eventQueue;

        private FixSizeQueue<EventItem> _alarmQueue;

        private PeriodicJob _eventJob;

        private EventDBWriter _eventDb;

        private EventLogWriter _writerToLog;

        private EventMailWriter _writerToMail;

        private EventService _eventService;

        private ServiceHost _eventServiceHost;

        /// <summary>
        /// 系统支持的事件类型。
        /// </summary>
        private readonly Dictionary<string, EventItem> _dictEvTypeSource = new();

        private List<AlarmEventItem> _alarms;

        #endregion

        #region Properties

        public EventService Service => _eventService;

        public List<VIDItem> VidEventList
        {
            get
            {
                var list = new List<VIDItem>();
                foreach (var item in _dictEvTypeSource)
                {
                    list.Add(new VIDItem
                    {
                        DataType = "",
                        Description = item.Value.Description,
                        Index = 0,
                        Name = item.Key,
                        Unit = ""
                    });
                }
                return list;
            }
        }

        public List<VIDItem> VidAlarmList
        {
            get
            {
                var list = new List<VIDItem>();
                foreach (var alarm in _alarms)
                {
                    list.Add(new VIDItem
                    {
                        DataType = "",
                        Description = alarm.Description,
                        Index = 0,
                        Name = alarm.EventEnum,
                        Unit = ""
                    });
                }
                return list;
            }
        }

        #endregion

        #region Methods

        public void Initialize(string commonEventListXmlFile, bool needCreateService = true, bool needSaveDb = true, bool needMailOut = false, string localEventListXmlFile = null)
        {
            InitData();
            InitOp();

            if (needSaveDb)
            {
                _eventDb = new EventDBWriter();
                try
                {
                    _eventDb.Initialize();
                }
                catch (Exception ex)
                {
                    LOG.Write(ex);
                }
            }
            _writerToLog = new EventLogWriter();
            if (needMailOut)
            {
                _writerToMail = new EventMailWriter();
            }
            _eventService = new EventService();
            if (needCreateService)
            {
                try
                {
                    _eventServiceHost = new ServiceHost(_eventService);
                    _eventServiceHost.Open();
                }
                catch (Exception ex2)
                {
                    throw new ApplicationException("创建Event服务失败，" + ex2.Message);
                }
            }
            _eventQueue = new FixSizeQueue<EventItem>(1000);
            _alarmQueue = new FixSizeQueue<EventItem>(1000);
            _alarms = new List<AlarmEventItem>(1000);
            _eventJob = new PeriodicJob(100, PeriodicRun, "EventPeriodicJob", isStartNow: true);
            try
            {
                var eventDefine = CustomXmlSerializer.Deserialize<EventDefine>(new FileInfo(commonEventListXmlFile));
                foreach (var item in eventDefine.Types)
                {
                    _dictEvTypeSource[item.EventEnum] = item;
                }
            }
            catch (ArgumentNullException)
            {
                throw new ApplicationException("初始化EventManager没有设置Event列表文件");
            }
            catch (FileNotFoundException ex4)
            {
                throw new ApplicationException("没有找到Event列表文件，" + ex4.Message);
            }
            catch (Exception ex5)
            {
                throw new ApplicationException("EventDefine文件格式不对，" + commonEventListXmlFile + ",\r\n" + ex5.Message);
            }
            try
            {
                if (!string.IsNullOrEmpty(localEventListXmlFile))
                {
                    var eventDefine2 = CustomXmlSerializer.Deserialize<EventDefine>(new FileInfo(localEventListXmlFile));
                    foreach (var item2 in eventDefine2.Types)
                    {
                        _dictEvTypeSource[item2.EventEnum] = item2;
                    }
                }
            }
            catch (ArgumentNullException)
            {
                throw new ApplicationException("初始化EventManager没有设置Event列表文件");
            }
            catch (FileNotFoundException ex)
            {
                throw new ApplicationException("没有找到Event列表文件，" + ex.Message);
            }
            catch (Exception ex)
            {
                throw new ApplicationException("EventDefine文件格式不对，" + localEventListXmlFile + ",\r\n" + ex.Message);
            }

            Subscribe(new EventItem(INFORMATION_EVENT, EventType.EventUI_Notify, EventLevel.Information));
            Subscribe(new EventItem(WARNING_EVENT, EventType.EventUI_Notify, EventLevel.Warning));
            Subscribe(new EventItem(ALARM_EVENT, EventType.EventUI_Notify, EventLevel.Alarm));
            EV.InnerEventManager = this;
        }

        private void InitData()
        {
            DATA.Subscribe("System.LiveAlarmEvent", GetAlarmEvent);

            DATA.Subscribe("System.ActiveAlarm", () => _alarmQueue.ToList().FindAll(x => !x.IsAcknowledged));
            DATA.Subscribe("System.HasActiveAlarm", () => _alarmQueue.ToList().Any(x => !x.IsAcknowledged));
            
            DATA.Subscribe("System.HasActiveAlarmEvent", () => _alarmQueue.ToList().Any(x => x.Level == EventLevel.Alarm && !x.IsAcknowledged));
            DATA.Subscribe("System.HasAckedAlarmEvent", () => _alarmQueue.ToList().Any(x => x.Level == EventLevel.Alarm && x.IsAcknowledged));
            DATA.Subscribe("System.HasActiveWarningEvent", () => _alarmQueue.ToList().Any(x => x.Level == EventLevel.Warning && !x.IsAcknowledged));
            DATA.Subscribe("System.HasAckedWarningEvent", () => _alarmQueue.ToList().Any(x => x.Level == EventLevel.Warning && x.IsAcknowledged));
        }

        private void InitOp()
        {
            OP.Subscribe("System.ResetAlarm", InvokeResetAlarm);

            #region 以下为用于测试Event的操作

            OP.Subscribe("System.Diagnosis.GenWarningEvent", (s, args) =>
            {
                PostWarningLog(ModuleName.System.ToString(), args[0].ToString());
                return true;
            });

            OP.Subscribe("System.Diagnosis.GenAlarmEvent", (s, args) =>
            {
                PostAlarmLog(ModuleName.System.ToString(), args[0].ToString());
                return true;
            });

            OP.Subscribe("System.Diagnosis.GenPjDoneEvent", (s, args) =>
            {
                //WriteEvent(ModuleName.System.ToString(), "PJ_DONE", "LoadLock", "0");
                //EV.PostMessage(ModuleName.System.ToString(), EventEnum.PJ_DONE, "LoadLock", "0");
                OP.DoOperation("System.AlertJobDone", ModuleName.LoadLock, 0);
                return true;
            });
            
            #endregion
        }

      
        /// <summary>
        /// 复位指定的Alarm。
        /// <remarks>
        /// 请参考UI.Client库中的ModuleAlarmViewModel对象。
        /// <br/>
        /// 在此视图中，可以复位单条Alarm.
        /// </remarks>
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <returns></returns>
        private bool InvokeResetAlarm(string arg1, object[] arg2)
        {
            ClearAlarmEvent();
            if (arg2 != null && arg2.Length >= 2)
            {
                _alarms.FirstOrDefault(x => x.Source == (string)arg2[0] && x.EventEnum == (string)arg2[1])?.Reset();
            }
            return true;
        }

        /// <summary>
        /// 终止事件管理器。
        /// </summary>
        public void Terminate()
        {
            if (_eventJob != null)
            {
                _eventJob.Stop();
                _eventJob = null;
            }
            if (_eventServiceHost != null)
            {
                _eventServiceHost.Close();
                _eventServiceHost = null;
            }
        }

        /// <summary>
        /// 创建指定的事件并将其写入列队。
        /// </summary>
        /// <param name="eventName">事件名称。</param>
        public void WriteEvent(string eventName)
        {
            if (!_dictEvTypeSource.ContainsKey(eventName))
            {
                LOG.Write("Event name not registered, " + eventName);
            }
            else
            {
                WriteEvent(_dictEvTypeSource[eventName].Source, eventName);
            }
        }

        /// <summary>
        /// 创建指定的事件并将其写入列队。
        /// </summary>
        /// <param name="module">事件所属模组名称。</param>
        /// <param name="eventName">事件名称。</param>
        /// <param name="message">事件消息。</param>
        public void WriteEvent(string module, string eventName, string message)
        {
            if (!_dictEvTypeSource.ContainsKey(eventName))
            {
                LOG.Write("Event name not registered, " + eventName);
                return;
            }
            var eventItem = _dictEvTypeSource[eventName].Clone();
            eventItem.Source = module;
            eventItem.Description = message;
            eventItem.OccuringTime = DateTime.Now;
            _eventQueue.Enqueue(eventItem);
            if (eventItem.Level == EventLevel.Alarm || eventItem.Level == EventLevel.Warning)
            {
                _alarmQueue.Enqueue(eventItem);
                OnAlarmEvent?.Invoke(eventItem);

            }

            OnEvent?.Invoke(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        /// <summary>
        /// 创建指定的事件并将其写入列队。
        /// </summary>
        /// <param name="eventName">事件名称。</param>
        /// <param name="dvid"></param>
        public void WriteEvent(string eventName, SerializableDictionary<string, string> dvid)
        {
            if (!_dictEvTypeSource.ContainsKey(eventName))
            {
                LOG.Write("Event name not registered, " + eventName);
            }
            else
            {
                WriteEvent(_dictEvTypeSource[eventName].Source, eventName, dvid);
            }
        }

        public void WriteEvent(string eventName, SerializableDictionary<string, object> dvid)
        {
            if (!_dictEvTypeSource.ContainsKey(eventName))
            {
                LOG.Error("Event name not registered, " + eventName);
                return;
            }
            var eventItem = _dictEvTypeSource[eventName].Clone(dvid);
            eventItem.OccuringTime = DateTime.Now;
            ProceedReceivedEvent(eventItem);
        }

        public void WriteEvent(string module, string eventName, params object[] args)
        {
            var eventItem = _dictEvTypeSource[eventName].Clone();
            eventItem.Source = module;
            if (_dictEvTypeSource[eventName].Description == null)
            {
                return;
            }
            eventItem.Description = string.Format(_dictEvTypeSource[eventName].Description, args);
            if (!string.IsNullOrEmpty(_dictEvTypeSource[eventName].GlobalDescription_en))
            {
                eventItem.GlobalDescription_en = string.Format(_dictEvTypeSource[eventName].GlobalDescription_en, args);
            }
            if (!string.IsNullOrEmpty(_dictEvTypeSource[eventName].GlobalDescription_zh))
            {
                eventItem.GlobalDescription_zh = string.Format(_dictEvTypeSource[eventName].GlobalDescription_zh, args);
            }
            eventItem.OccuringTime = DateTime.Now;
            _eventQueue.Enqueue(eventItem);
            if (eventItem.Level == EventLevel.Alarm || eventItem.Level == EventLevel.Warning)
            {
                _alarmQueue.Enqueue(eventItem);
                OnAlarmEvent?.Invoke(eventItem);
            }

            OnEvent?.Invoke(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        public void WriteEvent(string module, string eventName, SerializableDictionary<string, string> dvid, params object[] args)
        {
            var eventItem = _dictEvTypeSource[eventName].Clone();
            eventItem.Source = module;
            eventItem.Description = string.Format(_dictEvTypeSource[eventName].Description, args);
            if (!string.IsNullOrEmpty(_dictEvTypeSource[eventName].GlobalDescription_en))
            {
                eventItem.GlobalDescription_en = string.Format(_dictEvTypeSource[eventName].GlobalDescription_en, args);
            }
            if (!string.IsNullOrEmpty(_dictEvTypeSource[eventName].GlobalDescription_zh))
            {
                eventItem.GlobalDescription_zh = string.Format(_dictEvTypeSource[eventName].GlobalDescription_zh, args);
            }
            eventItem.OccuringTime = DateTime.Now;
            eventItem.DVID = dvid;
            _eventQueue.Enqueue(eventItem);
            if (eventItem.Level == EventLevel.Alarm || eventItem.Level == EventLevel.Warning)
            {
                _alarmQueue.Enqueue(eventItem);
                OnAlarmEvent?.Invoke(eventItem);
            }

            OnEvent?.Invoke(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        private void ProceedReceivedEvent(EventItem item)
        {
            _eventQueue.Enqueue(item);
            if (item.Level == EventLevel.Alarm || item.Level == EventLevel.Warning)
            {
                _alarmQueue.Enqueue(item);
                OnAlarmEvent?.Invoke(item);
            }

            OnEvent?.Invoke(item);
            _writerToLog.WriteEvent(item);
        }

        public void PostNotificationMessage(string message)
        {
            var eventItem = new EventItem
            {
                Type = EventType.UIMessage_Notify,
                Description = message,
                OccuringTime = DateTime.Now
            };
            _eventQueue.Enqueue(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        public void PostPopDialogMessage(EventLevel level, string title, string message)
        {
            var eventItem = new EventItem
            {
                Type = EventType.Dialog_Nofity,
                Description = title,
                Explaination = message,
                OccuringTime = DateTime.Now,
                Level = level
            };
            _eventQueue.Enqueue(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        public void PostKickoutMessage(string message)
        {
            var eventItem = new EventItem
            {
                Type = EventType.KickOut_Notify,
                Description = message,
                OccuringTime = DateTime.Now
            };
            _eventQueue.Enqueue(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        public void PostSoundMessage(string message)
        {
            var eventItem = new EventItem
            {
                Type = EventType.Sound_Notify,
                Description = message,
                OccuringTime = DateTime.Now
            };
            _eventQueue.Enqueue(eventItem);
            _writerToLog.WriteEvent(eventItem);
        }

        private bool PeriodicRun()
        {
            while (_eventQueue.TryDequeue(out var ev))
            {
                try
                {
                    _eventDb?.WriteEvent(ev);
                    _writerToMail?.WriteEvent(ev);
                    _eventService?.FireEvent(ev);
                    FireEvent?.Invoke(ev);
                }
                catch (Exception ex)
                {
                    LOG.Error("Failed to post event", ex);
                }
            }
            return true;
        }

        /// <summary>
        /// 返回当前报警事件列表。
        /// </summary>
        /// <returns></returns>
        public List<EventItem> GetAlarmEvent()
        {
            return _alarmQueue.ToList();
        }

        /// <summary>
        /// 将当前事件管理器中的所有报警事件设置为已响应。
        /// </summary>
        public void AckAlarmEvents()
        {
            _alarmQueue.ToList().ForEach(x=>
            {
                x.IsAcknowledged = true;
                x.AcknowledgedTime = DateTime.Now;
            });
        }

        /// <summary>
        /// 清楚当前报警事件列表。
        /// </summary>
        public void ClearAlarmEvent()
        {
            _alarmQueue.Clear();
        }

        public List<EventItem> QueryDBEvent(string sql)
        {
            return _eventDb.QueryDBEvent(sql);
        }

        public void Subscribe(EventItem item)
        {
            if (!_dictEvTypeSource.ContainsKey(item.EventEnum))
            {
                _dictEvTypeSource[item.EventEnum] = item;
                if (item is AlarmEventItem eventItem)
                {
                    _alarms.Add(eventItem);
                }
            }
        }

        public void PostInfoLog(string module, string message)
        {
            WriteEvent(module, INFORMATION_EVENT, message);
        }

        public void PostWarningLog(string module, string message)
        {
            WriteEvent(module, WARNING_EVENT, message);
        }

        public void PostAlarmLog(string module, string message)
        {
            try
            {
                WriteEvent(module, ALARM_EVENT, message);


                if (GemManager.Instance.Equipment != null)
                {
                    //如果不是ModuleName中定义的,统一归于System
                    if (!Enum.TryParse(module, out ModuleName moduleName))
                    {
                        module = "System";
                    }

                    var alarm = GemManager.Instance.Equipment.GetAlarm(module + ".Alarm");
                    if (alarm != null)
                    {
                        GemManager.Instance.Equipment?.SetAlarm(alarm.ALID, "", new string[] { module + ".AlarmInfo" }, new object[] { message });
                    }
                }
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message, ex);
            }
        }

        #endregion
    }
}
