/*-------------------------------------------------------------------------
//                           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 Raytheon.Instruments.PowerSupplies;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Raytheon.Instruments
{
   /// <summary>
   /// Base class that defines interface for power supply modules
   /// </summary>
   public abstract class PowerSupplyModule
   {
      protected Semaphore SemObj = new System.Threading.Semaphore(initialCount: 1, maximumCount: 1);
      protected object SyncObj = new object();
      protected string ActivePowerModule;
      
      public List<string> PowerModules { get; protected set; }

      public Dictionary<string, PowerSupplyModuleInfo> PowerModuleInfoDict {  get; protected set; }

      /// <summary>
      /// Constructor
      /// </summary>
      public PowerSupplyModule()
      {
         PowerModuleInfoDict = new Dictionary<string, PowerSupplyModuleInfo>();
      }

      /// <summary>
      /// DisplayEnabled - some instruments will be no-op, but likely needed on all
      ///                  will shut down any display on the hardware to hide any
      ///                  classified information if visible
      /// </summary>
      public virtual bool DisplayEnabled
      {
         get;
         set;
      }

      /// <summary>
      /// FrontPanelEnabled - some instruments will be no-op, but likely needed on all
      ///                     has the ability to disable any panel buttons, so that
      ///                     the hardware may not be changed by human touch
      /// </summary>
      public virtual bool FrontPanelEnabled
      {
         private get;
         set;
      }

      /// <summary>
      /// SelfTestResult - end result of the hardware self test performed
      /// </summary>
      public SelfTestResult SelfTestResult
      {
         get;
         set;
      }

      /// <summary>
      /// PerformSelfTest - do a hardware self test
      /// </summary>
      /// <returns>result of the self test</returns>
      public abstract SelfTestResult PerformSelfTest();

      /// <summary>
      /// Set active module
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      public void SetActivePowerModule(string activePowerModule)
      {
         ActivePowerModule = activePowerModule;
      }

      /// <summary>
      /// Check if active module is valid
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected void CheckActivePowerModuleValidity()
      {
         if (ActivePowerModule == null || ActivePowerModule.Length == 0)
            throw new Exception($"{nameof(ActivePowerModule)} is not set");

         if (!PowerModuleInfoDict.ContainsKey(ActivePowerModule))
            throw new Exception($"Invalid power module: {ActivePowerModule}");
      }

      /// <summary>
      /// Return semaphore
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      public Semaphore GetSemphamore() { return SemObj; }

      /// <summary>
      /// Turn on power module's output
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      public abstract void On();

      /// <summary>
      /// Turn off power module's output
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      public abstract void Off();

      /// <summary>
      /// Determine if module's output is on
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected virtual bool IsOutputOn()
      {
         bool isOn = false;

         lock (SyncObj)
         {
            CheckActivePowerModuleValidity();

            isOn = PowerModuleInfoDict[ActivePowerModule].isOn_;
         }

         return isOn;
      }

      /// <summary>
      /// Read power supply module's data all at once
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      public virtual void ReadPowerSupplyData(out double volts, out double current, out double voltSetpoint, out bool isOn, out int faultStatus)
      {
         volts = 0.0;
         current = 0.0;
         voltSetpoint = 0.0;
         isOn = false;
         faultStatus = 0;
         try
         {
            lock (SyncObj)
            {
               CheckActivePowerModuleValidity();

               volts = ReadVoltage();
               current = ReadCurrent();
               voltSetpoint = PowerModuleInfoDict[ActivePowerModule].voltageSetpoint_;
               isOn = IsOutputOn();
               faultStatus = ReadProtectionStatus();
            }
         }
         finally { SemObj?.Release(); };
      }

      /// <summary>
      /// Read voltage
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected abstract double ReadVoltage();

      /// <summary>
      /// Read current
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected abstract double ReadCurrent();

      /// <summary>
      /// Read protection status
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected abstract int ReadProtectionStatus();

      /// <summary>
      /// Get error code
      /// </summary>
      /// <param></param>
      /// <returns></returns>
      protected abstract string GetErrorCode(out int errorCode);
   }
}
