/*-------------------------------------------------------------------------
//                           UNCLASSIFIED
/*-------------------------------------------------------------------------
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.

THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
GOVERNMENT.

UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/
using NLog;
using Raytheon.Common;
using Raytheon.Instruments;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Raytheon
{
   /// <summary>
   /// Class for controlling all power supplies
   /// </summary>
   public class PowerSupplyMeasurementManager
   {
      #region PrivateClassMembers
      /// <summary>
      /// NLog logger
      /// </summary>
      private static NLog.ILogger _logger;

      private IConfigurationFile _powerOffAndSelfTestConfig;

      private IInstrumentManager _instrumentManager;

      private Dictionary<string, PowerModuleMeasurementManager> _powerSystemNameToPowerModuleMeasurementManagerDict = new Dictionary<string, PowerModuleMeasurementManager>();

      string _powerSystemWithFailedSelfTest = String.Empty;

      #endregion

      /// <summary>
      /// constructor
      /// </summary>
      /// <param name="stePowerSupplyInstanceName">the name specified in the Instruments.xml file</param>
      public PowerSupplyMeasurementManager(IInstrumentManager instrumentManager, string powerSupplySelfTestLogFile)
      {
         _logger = LogManager.GetCurrentClassLogger();

         _powerOffAndSelfTestConfig = new ConfigurationFile(powerSupplySelfTestLogFile);

         _instrumentManager = instrumentManager;
      }

      /// <summary>
		/// The Finalizer
		/// </summary>
		~PowerSupplyMeasurementManager()
      {
         _logger?.Debug($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() ...");
      }

      /// <summary>
      /// Implement Indexer to obtain a powermodulemeasurementmanager
      /// </summary>
      /// <param name=""></param>
      /// <returns></returns>
      public PowerModuleMeasurementManager this[object powerSystemId]
      {
         get
         {
            string powerSystemName;
            if (powerSystemId.GetType().IsEnum || powerSystemId is string)
            {
               powerSystemName = powerSystemId.ToString();
            }
            else
            {
               throw new ArgumentException($"{nameof(powerSystemId)} must be an enumerated or string type");
            }

            if (!_powerSystemNameToPowerModuleMeasurementManagerDict.ContainsKey(powerSystemName))
            {
               throw new Exception($"Invalid power supply system: {powerSystemName}");
            }

            return _powerSystemNameToPowerModuleMeasurementManagerDict[powerSystemName];
         }
      }

      /// <summary>
      /// Initialize the instrument(s)
      /// </summary>
      public void Initialize()
      {
         _logger.Trace($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() method...");

         PerformSelfTest();

         if (_powerSystemWithFailedSelfTest != String.Empty) 
         {
            throw new Exception($"{_powerSystemWithFailedSelfTest}'s self-test failed.");
         }
      }

      /// <summary>
      /// Perform self test on power supply system
      /// Self test for each power system takes a while, so we don't want to run self test every time we initialize the power system
      /// So we only want to run self test under 2 conditions:
      ///    1. Certain time has elapsed since last power off
      ///    2. Certain time has elapsed since last self test run ( in the absence of power off time)
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      private async void PerformSelfTest()
      {
         _logger.Trace($"Entering {this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() method...");

         string errorMsg = String.Empty;

         Dictionary<string, Task<SelfTestResult>> powerSystemToSelfTestTaskDict = new Dictionary<string, Task<SelfTestResult>>();

         bool allSelfTestsPassed = true;

         ICollection<object> psList = _instrumentManager.GetInstruments(typeof(PowerSupply));

         foreach (PowerSupply ps in psList)
         {
            ps.Initialize();

            // perform self test on power system
            Task<SelfTestResult> task = PerformSelfTestTask(ps);
            powerSystemToSelfTestTaskDict[ps.Name] = task;
         }

         foreach (var item in powerSystemToSelfTestTaskDict)
         {
            // wait for self test on power system to finish
            SelfTestResult result = await item.Value;

            if (result == SelfTestResult.Fail && String.IsNullOrEmpty(_powerSystemWithFailedSelfTest))
            {
               allSelfTestsPassed = false;
               _powerSystemWithFailedSelfTest = item.Key;
            }
         }

         if (allSelfTestsPassed)
         {
            foreach (PowerSupply ps in psList)
            {
               _powerSystemNameToPowerModuleMeasurementManagerDict[ps.Name] = new PowerModuleMeasurementManager(ps, _powerOffAndSelfTestConfig);
            }
         }
      }

      /// <summary>
      /// Perform self test on power supply system
      /// Self test for each power system takes a while, so we don't want to run self test every time we initialize the power system
      /// So we only want to run self test under 2 conditions:
      ///    1. Certain time has elapsed since last power off
      ///    2. Certain time has elapsed since last self test run ( in the absence of power off time)
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      private Task<SelfTestResult> PerformSelfTestTask(PowerSupply ps)
      {
         SelfTestResult result = SelfTestResult.Pass;

         _logger?.Debug($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() for {ps.Name} is running...");

         try
         {
            bool performSelfTest = false;
            const int HOURS_ELAPSED = 2;

            string lastSaveTime = MeasurementManager.PowerSupply.Util.GetLastSavedTime(ps.Name, _powerOffAndSelfTestConfig);

            if (DateTime.TryParse(lastSaveTime, out DateTime dt))
            {
               // if this power supply system has been turn off for a certain number of hours, then we want to perform self test on it
               if (DateTime.Now.Subtract(dt).TotalHours >= HOURS_ELAPSED)
               {
                  performSelfTest = true;
               }
            }
            else { performSelfTest = true; }

            if (performSelfTest)
            {
               _logger?.Info($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() executing self-test for {ps.Name}...");

               result = ps[null].PerformSelfTest();

               if (result == SelfTestResult.Pass)
               {
                  // save the time of this self test to file
                  MeasurementManager.PowerSupply.Util.SaveTime(ps.Name, null, DateTime.Now.ToString(), _powerOffAndSelfTestConfig);
               }
            }
            else
               _logger?.Debug($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() skipping self-test for {ps.Name}...");
         }
         catch (Exception ex)
         {
            _logger?.Error(ex.Message + "\n" + ex.StackTrace);
         }

         _logger?.Debug($"{this.GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}() for {ps.Name} is exiting...");

         return Task.FromResult(result);
      }

      
   }
}
