// **********************************************************************************************************
//    BitGenSoftMeasurementManager.cs
//    7/28/2022
//    NGI - Next Generation Interceptor
//    
//    Contract No. HQ0856-21-C-0003/1022000209
//    
//    THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S. 
//    INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//    
//    RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON 
//    COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. 
//    DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON 
//    COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED 
//    TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF 
//    RAYTHEON COMPANY.
//    
//    UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//    
//    DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M, 
//    NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006, 
//    INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3, 
//    DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3, 
//    SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4, 
//    INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//    
//    CONTROLLED BY: MISSILE DEFENSE AGENCY
//    CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
//    CUI CATEGORY: CTI 
//    DISTRIBUTION/DISSEMINATION CONTROL: F 
//    POC: Alex Kravchenko (1118268)
// **********************************************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.XPath;
using NLog;
using Raytheon.Common;
using Raytheon.Common.Coe;
using Raytheon.Instruments;

namespace MeasurementManagerLib
{
   /// <summary>
   /// COE Measurement Manager class
   /// </summary>
   public class CoeMeasurementManager : IDisposable
   {
      private readonly ILogger _logger = LogManager.GetCurrentClassLogger();

      private readonly Dictionary<string, CoeComm> _coeNodes = new Dictionary<string, CoeComm>();

      private readonly IInstrumentManager _instrumentManager;
      public int CheckForMessageIntervalMs { get; private set; }
      /// <summary>
      /// constructor that will create a list of BIT instruments
      /// </summary>
      /// <param name="instrumentManager"></param>
      /// <param name="switchMeasurementManagerConfigFullPath"></param>
      public CoeMeasurementManager(IInstrumentManager instrumentManager, string coeMeasurementManagerConfigFullPath)
      {
         try
         {
            _instrumentManager = instrumentManager;

            ConfigurationFile configurationFile = new ConfigurationFile(coeMeasurementManagerConfigFullPath);

            CheckForMessageIntervalMs = 10;

            if (Int32.TryParse(configurationFile.ReadValue("GENERAL", "CheckForMessageIntervalMs"), out int val))
            {
               CheckForMessageIntervalMs = val;
            }

            ICollection<object> coeNodeList = _instrumentManager.GetInstruments(typeof(CoeComm));
            foreach (CoeComm node in coeNodeList)
            {
               _coeNodes.Add(node.Name, node);
            }
         }
         catch (Exception)
         {
            throw;
         }
      }

      ~CoeMeasurementManager()
      {
         _logger?.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() ...");
         foreach (var node in _coeNodes)
         {
            try
            {
               CoeComm coeNode = node.Value;
               coeNode.Shutdown();
            }
            catch { }
         }
      }

      /// <summary>
      /// Get XML Docs
      /// </summary>
      public object GetXmlDocs(string coeDevice)
      {
         return _coeNodes[coeDevice].GetXmlDocs();
      }

      /// <summary>
      /// Create a message
      /// </summary>
      public Message CreateMessage(string coeDevice, string messageName)
      {
         Message msg = null;

         MessageXmlDocument msgXmlDocument = null;

         Dictionary<string, MessageXmlDocument> xmlDocs = new Dictionary<string, MessageXmlDocument>();
         var xmlDocObject = GetXmlDocs(coeDevice);

         if (xmlDocObject != null && xmlDocObject.GetType() == xmlDocs.GetType())
         {
            xmlDocs = (Dictionary<string, MessageXmlDocument>)xmlDocObject;

            foreach (KeyValuePair<string, MessageXmlDocument> item in xmlDocs)
            {
               XPathNavigator Node = xmlDocs[item.Key].CreateNavigator();
               XPathNodeIterator Nodeset = Node.Select("interface/message/name");
               while (Nodeset.MoveNext())
               {
                  if (String.Equals(Nodeset.Current.Value, messageName, StringComparison.OrdinalIgnoreCase))
                  {
                     msgXmlDocument = item.Value;
                     break;
                  }
               }

               if (msgXmlDocument != null)
                  break;
            }
         }

         if (msgXmlDocument != null)
         {
            msg = new Message(messageName, msgXmlDocument, _coeNodes[coeDevice].ShallLogMessage(messageName));
            msg.Default();
         }

         return msg;
      }

      /// <summary>
      /// initialize COE nodes based on test type
      /// </summary>
      /// <param name="instrumentName"></param>
      /// <param name="testType"></param>
      public void InitNodes()
      {
         foreach (var node in _coeNodes)
         {
            try
            {
               CoeComm coeNode = node.Value;
               coeNode.Initialize();
               coeNode.Open();
            }
            catch (Exception)
            {
               throw;
            }
         }
      }

      /// <summary>
      /// close all connections
      /// </summary>
      /// <exception cref="NotImplementedException"></exception>
      public void Dispose()
      {
         foreach (var coeNode in _coeNodes)
         {
            coeNode.Value?.Shutdown();
         }
      }

      /// <summary>
      /// Send message using the first available device
      /// </summary>
      /// <param name="messageId"></param>
      /// <param name="timeoutInMs"></param>
      /// <param name="messageParams"></param>
      /// <returns></returns>
      public bool SendMessage(string messageId, IEnumerable<KeyValuePair<string, string>> messageParams = null)
      {
         if (_coeNodes.Any())
         {
            CoeComm coeNode = _coeNodes.First().Value;
            return coeNode.SendMessage(messageId, messageParams);
         }
         else
         {
            throw new Exception($"Unable to locate COE node. No nodes defined");
         }
      }

      /// <summary>
      /// Send message from a particular device
      /// </summary>
      /// <param name="instrumentName"></param>
      /// <param name="messageId"></param>
      /// <param name="timeoutInMs"></param>
      /// <param name="messageParams"></param>
      /// <returns></returns>
      public bool SendMessage(string instrumentName, string messageId, IEnumerable<KeyValuePair<string, string>> messageParams = null)
      {
         if (_coeNodes.ContainsKey(instrumentName))
         {
            return _coeNodes[instrumentName].SendMessage(messageId, messageParams);
         }
         else
         {
            throw new Exception($"Unable to locate COE node {instrumentName}");
         }
      }

      /// <summary>
      /// Get subscribed message from UUT using the first device
      /// </summary>
      /// <param name="messageId"></param>
      /// <returns></returns>
      public CoeResponseMsgData GetNextResponseInQueue(string messageId)
      {
         if (_coeNodes.Any())
         {
            CoeComm coeNode = _coeNodes.First().Value;
            return coeNode.GetNextResponseInQueue(messageId);
         }
         else
            return null;
      }

      /// <summary>
      /// Get subscribed message from UUT from a particular device
      /// </summary>
      /// <param name="instrumentName"></param>
      /// <param name="messageId"></param>
      /// <returns></returns>
      public CoeResponseMsgData GetNextResponseInQueue(string instrumentName, string messageId)
      {
         return _coeNodes.ContainsKey(instrumentName) ? _coeNodes[instrumentName].GetNextResponseInQueue(messageId) : null;
      }

      /// <summary>
      /// Clear the queue for a particular response message. 
      /// This is useful if we have a large size queue and we don't want to dequeue each item to get 
      /// to the newest item. So we clear the queue first, before trying to get the newest item in the queue
      /// </summary>
      public void ClearResponseMessageQueue(string instrumentName, string messageId)
      {
         if (_coeNodes.ContainsKey(instrumentName))
         {
            _coeNodes[instrumentName].ClearResponseMessageQueue(messageId);
         }
      }

   }
}
