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

namespace Aitex.Core.RT.Device
{
	public class DeviceManagerBase : IDeviceManager
	{

        #region Variables

        private readonly Dictionary<string, IDevice> _nameDevice = new();
        private readonly Dictionary<Type, List<IDevice>> _typeDevice = new();
        private readonly DeviceTimer _timer = new();
        private readonly R_TRIG _trigExceed500 = new();
        private readonly ConcurrentBag<IDevice> _optionDevice = new();
        private readonly object _lockerDevice = new();

        #endregion

        #region Constructors

        public DeviceManagerBase()
        {
            DEVICE.Manager = this;
        }

        #endregion

        #region Properties

		/// <summary>
		/// 返回当前设备管理器映射的XML配置文件节点。
		/// </summary>
        protected XmlElement DeviceModelNodes { get; private set; }

		/// <summary>
		/// 设置或返回是否以异步方式初始化当前管理器中的设备。
		/// </summary>
		public bool DisableAsyncInitialize { get; set; }

        #endregion
        
        #region Methods

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

 

        /// <summary>
        /// 向设备管理器的设备字典中添加一个设备实例。
        /// </summary>
        /// <param name="device"></param>
        protected virtual void QueueDevice(IDevice device)
        {
            QueueDevice(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);
            }
        }

        /// <summary>
		/// 终止当前对象。
		/// </summary>
		public virtual void Terminate()
		{
			lock (_lockerDevice)
			{
				foreach (var value in _nameDevice.Values)
				{
                    try
                    {
                        value.Terminate();
                    }
                    catch (Exception e)
                    {
                       LOG.Write(value.Name+e); 
                    }
					
				}
			}
		}

        /// <summary>
        /// 后台扫面线程调用的方法。
        /// </summary>
        public void 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");
                    }
                }
            }
        }

        public void Reset()
		{
			lock (_lockerDevice)
			{
				foreach (var value in _nameDevice.Values)
				{
					value.Reset();
				}
				_trigExceed500.RST = true;
			}
		}

		public void Initialize(string modelFile, string type, ModuleName mod = ModuleName.System, string ioModule = "", bool endCallInit = true)
		{
			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);
						});
					}
				}
				if (endCallInit)
				{
					Initialize();
				}
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
			}
		}

		public void Initialize(string modelFile, string modelType, string moduleName, string ioPath, string scPath)
		{
			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 != modelType)
				{
					throw new ApplicationException($"the type {attribute} in device mode file {modelFile} is different with the system type {modelType}");
				}
				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 type = assembly.GetType(text3);
					if (type == 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;
						}
						xmlElement3.SetAttribute("ioPath", ioPath);
						xmlElement3.SetAttribute("scPath", scPath);
						var device = Activator.CreateInstance(type, moduleName, xmlElement3, ioPath) as IDevice;
						if (xmlElement3.HasAttribute("option") && Convert.ToBoolean(xmlElement3.Attributes["option"]))
						{
							_optionDevice.Add(device);
							continue;
						}
						DATA.Subscribe(device, $"{device.Module}.{text2}.{device.Name}");
						QueueDevice(device);
						if (!_typeDevice.ContainsKey(type))
						{
							_typeDevice[type] = new List<IDevice>();
						}
						_typeDevice[type].Add(device);
						if (DisableAsyncInitialize)
						{
							InitDevice(device);
							continue;
						}
						var task = Task.Run(delegate
						{
							InitDevice(device);
						});
					}
				}
				Initialize();
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
			}
		}
		
		/// <summary>
		/// 向当前设备管理器中添加一个自定义设备。
		/// </summary>
		/// <param name="device">设备对象的实例。</param>
		/// <param name="groupType"></param>
		/// <param name="deviceType">设备对象实例的类型。</param>
		/// <returns></returns>
		public IDevice AddCustomDevice(IDevice device, string groupType, Type deviceType)
		{
			lock (_lockerDevice)
			{
				DATA.Subscribe(device, $"{device.Module}.{groupType}.{device.Name}");
				if (!string.IsNullOrEmpty(device.Module) && device.Module != ModuleName.System.ToString())
				{
					_nameDevice.Add(device.Module + "." + device.Name, device);
				}
				else
				{
					_nameDevice.Add(device.Name, device);
				}
				if (!_typeDevice.ContainsKey(deviceType))
				{
					_typeDevice[deviceType] = new List<IDevice>();
				}
				_typeDevice[deviceType].Add(device);
				device.Initialize();
			}
			return device;
		}

		public IDevice AddCustomDevice(IDevice device, Type deviceType)
		{
			lock (_lockerDevice)
			{
				try
				{
					DATA.Subscribe(device, $"{device.Module}.{device.Name}");
					_nameDevice.Add(device.Name, device);
					if (!_typeDevice.ContainsKey(deviceType))
					{
						_typeDevice[deviceType] = new List<IDevice>();
					}
					_typeDevice[deviceType].Add(device);
					device.Initialize();
				}
				catch (Exception ex)
				{
					LOG.Write(ex);
				}
			}
			return device;
		}

		public IDevice AddCustomDevice(IDevice device)
		{
			lock (_lockerDevice)
			{
				try
				{
					DATA.Subscribe(device, $"{device.Module}.{device.Name}");
					_nameDevice.Add(device.Name, device);
					if (!_typeDevice.ContainsKey(device.GetType()))
					{
						_typeDevice[device.GetType()] = new List<IDevice>();
					}
					_typeDevice[device.GetType()].Add(device);
					device.Initialize();
				}
				catch (Exception ex)
				{
					LOG.Write(ex);
				}
			}
			return device;
		}

		public IDevice AddCustomModuleDevice(IDevice device)
		{
			lock (_lockerDevice)
			{
				try
				{
					DATA.Subscribe(device, $"{device.Module}.{device.Name}");
					_nameDevice.Add(device.Module + "." + device.Name, device);
					if (!_typeDevice.ContainsKey(device.GetType()))
					{
						_typeDevice[device.GetType()] = new List<IDevice>();
					}
					_typeDevice[device.GetType()].Add(device);
					device.Initialize();
				}
				catch (Exception ex)
				{
					LOG.Write(ex);
				}
			}
			return device;
		}

		public virtual bool Initialize()
		{
			return true;
		}

		/// <summary>
		/// 获取指定名称和类型的设备的实例。
		/// </summary>
		/// <param name="name">设备名称。</param>
		/// <typeparam name="T">设备类型。</typeparam>
		/// <returns></returns>
		public T GetDevice<T>(string name) where T : class, IDevice
		{
			if (!_nameDevice.ContainsKey(name))
			{
				return null;
			}
			return _nameDevice[name] as T;
		}

        /// <summary>
        /// 获取指定名称的设备的实例。
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object GetDevice(string name)
		{
			if (!_nameDevice.ContainsKey(name))
			{
				return null;
			}
			return _nameDevice[name];
		}

        /// <summary>
        /// 获取包含所有指定类型的设备实例的列表。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<T> GetDevice<T>() where T : class, IDevice
		{
			if (!_typeDevice.ContainsKey(typeof(T)))
			{
				return null;
			}
			var list = new List<T>();
			foreach (var item in _typeDevice[typeof(T)])
			{
				list.Add(item as T);
			}
			return list;
		}

		/// <summary>
		/// 获取当前设备管理器中的所有设备的列表。
		/// </summary>
		/// <returns></returns>
		public List<IDevice> GetAllDevice()
		{
			return _nameDevice.Values.ToList();
		}

		public object GetOptionDevice(string name, Type type)
		{
			foreach (var item in _optionDevice)
			{
				if (item.Module + "." + item.Name == name && (type == null || item.GetType() == type))
				{
					return item;
				}
			}
			return null;
		}


        #endregion
    }
}
