//                           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 System;
using System.Threading;
using ChillerCartMeasurementManagerLib;
using NLog;
using Raytheon.Instruments;

namespace MeasurementManagerLib
{
   /// <summary>
   /// This class manages chiller instruments and provides an abstraction
   /// </summary>
   public class ChillerCartMeasurementManager : IDisposable
   {
      #region PublicMembers
      public delegate void ChillerDelegate(double temperature, double coolantSetpoint, int errorCode);
      public delegate void FlowMeterDelegate(double flow, int errorCode);
      public delegate void TemperatureDelegate(double temperature, int errorCode);
      #endregion

      #region PrivateMembers
      private readonly IChiller _chiller;
      private readonly IFlowMeter _flowMeter;
      private readonly ITempMonitor _tempMonitor;
      private ChillerDataLogWorker _chillerDataLogWorker;
      private Thread _chillerDataLogThread;
      private FlowMeterDataLogWorker _flowMeterDataLogWorker;
      private Thread _flowMeterDataLogThread;
      private TempDataLogWorker _tempMonDataLogWorker;
      private Thread _tempMonDataLogThread;

      private static NLog.ILogger _logger;

      #endregion

      #region PrivateFunctions

      /// <summary>
      /// Dispose of this object's resources
      /// </summary>
      /// <param name="disposing">Currently disposing</param>
      protected virtual void Dispose(bool disposing)
      {
         if (disposing)
         {
            // stop the logging if it is still running
            try
            {
               ChillerLogStop();
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // stop the logging if it is still running
            try
            {
               FlowMeterLogStop();
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // stop the logging if it is still running
            try
            {
               TemperatureSensorLogStop();
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // dispose the thread
            try
            {
               if (_chillerDataLogWorker != null)
               {
                  _chillerDataLogWorker.Dispose();
               }
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // dispose the thread
            try
            {
               if (_flowMeterDataLogWorker != null)
               {
                  _flowMeterDataLogWorker.Dispose();
               }
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // dispose the thread
            try
            {
               if (_tempMonDataLogWorker != null)
               {
                  _tempMonDataLogWorker.Dispose();
               }
            }
            catch (Exception err)
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }

            // dispose other resources
            if (_chiller != null)
            {
               try
               {
                  _chiller.Shutdown();
               }
               catch (Exception err)
               {
                  _logger?.Error(err.Message + "\r\n" + err.StackTrace);
               }
            }

            if (_flowMeter != null)
            {
               try
               {
                  _flowMeter.Shutdown();
               }
               catch (Exception err)
               {
                  _logger?.Error(err.Message + "\r\n" + err.StackTrace);
               }
            }

            if (_tempMonitor != null)
            {
               try
               {
                  _tempMonitor.Shutdown();
               }
               catch (Exception err)
               {
                  _logger?.Error(err.Message + "\r\n" + err.StackTrace);
               }
            }
         }

      }

      /// <summary>
      /// The Finalizer
      /// </summary>
      ~ChillerCartMeasurementManager()
      {
         Dispose(false);
      }

      #endregion

      #region PublicFunctions

      /// <summary>
      /// constructor that uses instrument manager to create an instrument
      /// </summary>
      /// <param name="instrumentManager"></param>
      /// <param name="chillerName"></param>
      /// <param name="flowMeeterName"></param>
      /// <param name="tempSensorName"></param>
      /// <param name="isThereHardware"></param>
      /// <param name="errorLog"></param>
      public ChillerCartMeasurementManager(IInstrumentManager instrumentManager, string chillerName, string flowMeeterName, string tempSensorName, string errorLog)
      {
         try
         {
            _logger = LogManager.GetCurrentClassLogger();

            // these gets created in the start logging functions
            _chillerDataLogWorker = null;
            _chillerDataLogThread = null;
            _flowMeterDataLogWorker = null;
            _flowMeterDataLogThread = null;
            _tempMonDataLogWorker = null;
            _tempMonDataLogThread = null;

            _chiller = null;
            _flowMeter = null;
            _tempMonitor = null;

            if (!string.IsNullOrEmpty(chillerName))
            {
               _chiller = instrumentManager.GetInstrument<IChiller>(chillerName);
               _chiller?.Initialize();
            }

            if (!string.IsNullOrEmpty(flowMeeterName))
            {
               _flowMeter = instrumentManager.GetInstrument<IFlowMeter>(flowMeeterName);
               _flowMeter?.Initialize();
            }

            if (!string.IsNullOrEmpty(tempSensorName))
            {
               _tempMonitor = instrumentManager.GetInstrument<ITempMonitor>(tempSensorName);
               _tempMonitor?.Initialize();
            }
         }
         catch (Exception)
         {
            Dispose(true);

            throw;
         }
      }



      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public double ChillerGetCoolantTemperature()
      {
         return _chiller.GetCoolantTemperature();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public double ChillerGetCoolantTemperatureSetpoint()
      {
         return _chiller.GetCoolantSetpoint();
      }

      /// <summary>
      /// 
      /// </summary>
      public void ChillerDisableFlow()
      {
         _chiller.DisableFlow();
      }

      /// <summary>
      /// 
      /// </summary>
      public void ChillerEnableFlow()
      {
         _chiller.EnableFlow();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="filename"></param>
      /// <param name="threadRestTimeMs"></param>
      /// <param name="callback">An optional delegate for the host to receive the data</param>
      public void ChillerLogStart(string filename, int threadRestTimeMs, ChillerCartMeasurementManager.ChillerDelegate callback)
      {
         if (_chillerDataLogWorker != null)
         {
            //Should not be logging. Stop Logger
            ChillerLogStop();
            _chillerDataLogWorker.Dispose();
         }

         //Start logging on a new thread. Also, start calling callback with requested data
         _chillerDataLogWorker = new ChillerDataLogWorker(this, filename, threadRestTimeMs, callback);
         _chillerDataLogThread = new Thread(_chillerDataLogWorker.DoWork);
         _chillerDataLogThread.Start();
      }

      /// <summary>
      /// 
      /// </summary>
      public void ChillerLogStop()
      {
         const int THREAD_QUIT_TIMEOUT = 3000;

         //Is the logger running
         if (_chillerDataLogWorker != null)
         {
            _chillerDataLogWorker.QuitWork();

            if ((_chillerDataLogThread != null) && (_chillerDataLogThread.IsAlive))
            {
               bool didThreadQuit = _chillerDataLogThread.Join(THREAD_QUIT_TIMEOUT);

               if (didThreadQuit == false)
               {
                  _logger?.Error("Logging Thread did not quit as expected, aborting it");
                  _chillerDataLogThread.Abort();
               }
               else
               {
                  _logger?.Info("Logging Thread quit successfully after join");
               }
            }
            else
            {
               _logger?.Info("Logging Thread quit successfully");
            }
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="setpoint"></param>
      public void ChillerSetCoolantTemperature(double setpoint)
      {
         _chiller.SetCoolantTemperature(setpoint);
      }

      /// <summary>
      /// 
      /// </summary>
      public void Dispose()
      {
         try
         {
            Dispose(true);

            GC.SuppressFinalize(this);
         }
         catch (Exception err)
         {
            try
            {
               _logger?.Error(err.Message + "\r\n" + err.StackTrace);
            }
            catch (Exception)
            {
               //Do not rethrow. Exception from error logger that has already been garbage collected
            }
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="filename"></param>
      /// <param name="threadRestTimeMs"></param>
      /// <param name="callback">An optional delegate for the host to receive the data</param>
      public void FlowMeterLogStart(string filename, int threadRestTimeMs, ChillerCartMeasurementManager.FlowMeterDelegate callback)
      {
         if (_flowMeterDataLogWorker != null)
         {
            //Should not be logging. Stop logger
            FlowMeterLogStop();
            _flowMeterDataLogWorker.Dispose();
         }

         //Start logging on a new thread. Also, start calling callback with requested data
         _flowMeterDataLogWorker = new FlowMeterDataLogWorker(this, filename, threadRestTimeMs, callback);
         _flowMeterDataLogThread = new Thread(_flowMeterDataLogWorker.DoWork);
         _flowMeterDataLogThread.Start();
      }

      /// <summary>
      /// 
      /// </summary>
      public void FlowMeterLogStop()
      {
         const int THREAD_QUIT_TIMEOUT = 5000;

         //Are we logging
         if (_flowMeterDataLogWorker != null)
         {
            _flowMeterDataLogWorker.QuitWork();

            if ((_flowMeterDataLogThread != null) && (_flowMeterDataLogThread.IsAlive))
            {
               bool didThreadQuit = _flowMeterDataLogThread.Join(THREAD_QUIT_TIMEOUT);

               if (didThreadQuit == false)
               {
                  _logger?.Error("Logging Thread did not quit as expected, aborting it");
                  _flowMeterDataLogThread.Abort();
               }
               else
               {
                  _logger?.Info("Logging Thread quit successfully after join");
               }
            }
            else
            {
               _logger?.Info("Logging Thread quit successfully");
            }
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public double FlowMeterReadFlow()
      {
         return _flowMeter.ReadFlow();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public double TemperatureSensorReadTemperature()
      {
         return _tempMonitor.ReadTemperature();
      }

      /// <summary>
      /// Start the temperature sensor log thread
      /// </summary>
      /// <param name="filename"></param>
      /// <param name="threadRestTimeMs"></param>
      /// <param name="callback">An optional delegate for the host to receive the data</param>
      public void TemperatureSensorLogStart(string filename, int threadRestTimeMs, ChillerCartMeasurementManager.TemperatureDelegate callback)
      {
         if (_tempMonDataLogWorker != null)
         {
            //Should not be logging. Stop logger
            TemperatureSensorLogStop();
            _tempMonDataLogWorker.Dispose();
         }

         //Start logging on a new thread. Also, start calling callback with requested data
         _tempMonDataLogWorker = new TempDataLogWorker(this, filename, threadRestTimeMs, callback);
         _tempMonDataLogThread = new Thread(_tempMonDataLogWorker.DoWork);
         _tempMonDataLogThread.Start();
      }

      /// <summary>
      /// Stop the temperature sensor log thread
      /// </summary>
      public void TemperatureSensorLogStop()
      {
         const int THREAD_QUIT_TIMEOUT = 3000;

         //Are we logging
         if (_tempMonDataLogWorker != null)
         {
            _tempMonDataLogWorker.QuitWork();

            if ((_tempMonDataLogThread != null) && (_tempMonDataLogThread.IsAlive))
            {
               bool didThreadQuit = _tempMonDataLogThread.Join(THREAD_QUIT_TIMEOUT);

               if (didThreadQuit == false)
               {
                  _logger?.Error("Logging Thread did not quit as expected, aborting it");
                  _tempMonDataLogThread.Abort();
               }
               else
               {
                  _logger?.Info("Logging Thread quit successfully after join");
               }
            }
            else
            {
               _logger?.Info("Logging Thread quit successfully");
            }

         }
      }

      #endregion
   }
}
