using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Utilities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace MECF.Framework.Simulator.Core
{
    public class SimulatorDeviceManager
    {
        private readonly Dictionary<string, IDevice> _nameDevice = new Dictionary<string, IDevice>();
        private readonly object _lockerDevice = new object();
        private readonly R_TRIG _trigExceed500 = new R_TRIG();
        private readonly Dictionary<Type, List<IDevice>> _typeDevice = new Dictionary<Type, List<IDevice>>();
        private readonly ConcurrentBag<IDevice> _optionDevice = new ConcurrentBag<IDevice>();
        private PeriodicJob _thread;

        /// <summary>
        /// 设置或返回是否以异步方式初始化当前管理器中的设备。
        /// </summary>
        public bool DisableAsyncInitialize { get; set; }
        /// <summary>
        /// 返回当前设备管理器映射的XML配置文件节点。
        /// </summary>
        protected XmlElement DeviceModelNodes { get; private set; }


        /// <summary>
        /// 向设备管理器的设备字典中添加一个设备实例。
        /// </summary>
        /// <param name="device"></param>
        protected virtual void QueueDevice(IDevice device)
        {
            QueueDevice($"{device.Module}.{device.Name}", device);
        }

        /// <summary>
        /// 向设备管理器的设备字典中添加一个设备实例，并自定义该设备在字典中的Key。
        /// </summary>
        /// <param name="key"></param>
        /// <param name="device"></param>
        protected void QueueDevice(string key, IDevice device)
        {
            lock (_lockerDevice)
            {
                Debug.Assert(!_nameDevice.ContainsKey(key), "DeviceModel config file contains duplicated name " + key);
                _nameDevice.Add(key, device);
            }
        }

        public void Initialize(string modelFile, string type, ModuleName mod, string ioModule = "")
        {
            if (!File.Exists(modelFile))
            {
                throw new ApplicationException($"did not find the device model file {modelFile} ");
            }
            var xmlDocument = new XmlDocument();
            try
            {
                xmlDocument.Load(modelFile);
                if (!(xmlDocument.SelectSingleNode("DeviceModelDefine") is XmlElement xmlElement))
                {
                    throw new ApplicationException($"device mode file {modelFile} is not valid");
                }
                var attribute = xmlElement.GetAttribute("type");
                if (attribute != type)
                {
                    throw new ApplicationException($"the type {attribute} in device mode file {modelFile} is inaccordance with the system type {type}");
                }
                DeviceModelNodes = xmlElement;
                foreach (XmlNode childNode in xmlElement.ChildNodes)
                {
                    if (childNode.NodeType == XmlNodeType.Comment || !(childNode is XmlElement xmlElement2))
                    {
                        continue;
                    }
                    var text = xmlElement2.GetAttribute("assembly");
                    if (string.IsNullOrEmpty(text))
                    {
                        text = "MECF.Framework.RT.EquipmentLibrary";
                    }
                    var text2 = xmlElement2.Name.Substring(0, xmlElement2.Name.Length - 1);
                    var text3 = xmlElement2.GetAttribute("classType");
                    if (string.IsNullOrEmpty(text3))
                    {
                        text3 = "Aitex.Core.RT.Device.Unit." + text2;
                    }
                    var assembly = Assembly.Load(text);
                    var type2 = assembly.GetType(text3);
                    if (type2 == null)
                    {
                        continue;
                    }
                    foreach (var childNode2 in xmlElement2.ChildNodes)
                    {
                        if (!(childNode2 is XmlElement xmlElement3))
                        {
                            LOG.Write("Device Model File contains non element node, " + (childNode2 as XmlNode).Value);
                            continue;
                        }
                        var device = Activator.CreateInstance(type2, mod.ToString(), xmlElement3, ioModule) as IDevice;
                        if (xmlElement3.HasAttribute("option") && Convert.ToBoolean(xmlElement3.Attributes["option"].Value))
                        {
                            _optionDevice.Add(device);
                            continue;
                        }
                        QueueDevice(device);
                        DATA.Subscribe(device, $"{device.Module}.{text2}.{device.Name}");
                        if (!_typeDevice.ContainsKey(type2))
                        {
                            _typeDevice[type2] = new List<IDevice>();
                        }
                        _typeDevice[type2].Add(device);
                        if (DisableAsyncInitialize)
                        {
                            InitDevice(device);
                            continue;
                        }
                        var task = Task.Run(delegate
                        {
                            InitDevice(device);
                        });
                    }
                }

                _thread = new PeriodicJob(200, Monitor, "SimulatorSystem", true);
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        private void InitDevice(IDevice device)
        {
            try
            {
                if (!device.Initialize())
                {
                    LOG.Write(device.Name + " initialize failed.");
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        /// <summary>
        /// 后台扫面线程调用的方法。
        /// </summary>
        public bool Monitor()
        {
            lock (_lockerDevice)
            {
                foreach (var value in _nameDevice.Values)
                {
                    try
                    {
                        PerformanceMonitor.MonitorRun(() =>
                        {
                            value.Monitor();
                            return true;
                        }, 500, _trigExceed500, out _, $"{value.Module}.{value.Name} monitor");
                    }
                    catch (Exception ex)
                    {
                        LOG.Write(ex, $"{ModuleName.Diagnosis} Monitor {value.Name} Exception");
                    }
                }
            }

            return true;
        }
    }
}
