//                           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 Raytheon.Instruments.Dmm;
using Raytheon.Units;
using System.IO.Ports;
using NLog;
using Raytheon.Common;

namespace Raytheon.Instruments
{
	/// <summary>
	/// A Space Electronics (Squib meter) of the DMM interface
	/// </summary>
	public class DMMSquibMeter101SQBRAK : IDmm, IDisposable
	{
		#region PrivateMemberVariables

		private string _name;
		private readonly string _address;
		private readonly SerialPort _serialPort;
		private MeasurementFunction _lastType;
		private double _lastRange;
		private double _lastResolution;
		private State _state;
		private SelfTestResult _selfTestResult;
		private byte[] _readBuffer;
		private string versionInfo;

		/// <summary>
		/// NLog logger
		/// </summary>
		private readonly ILogger _logger;
		/// <summary>
		/// Raytheon configuration
		/// </summary>
		private readonly IConfigurationManager _configurationManager;
		private readonly IConfiguration _configuration;

		#endregion

		#region PrivateFunctions

		/// <summary>
		/// Dispose of this objects resources
		/// </summary>
		/// <param name="disposing"></param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "_34461A")]
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				if (disposing)
				{
					if (_state == State.Ready)
					{
						IOWrite("RST"); // send reset command
						IOWrite("LM"); // sets squib meter to local mode (it is set to remote in initilize)

						_serialPort.Close();

						_state = State.Uninitialized;
					}
				}
			}
			catch (Exception)
			{
				try
				{
					//ErrorLogger.Instance().Write(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="data"></param>
		/// <returns></returns>
		private string ConvertToString(ref byte[] data)
		{
			string rsp = System.Text.Encoding.ASCII.GetString(data);
			return rsp;
		}

		/// <summary>
		/// Send a command to the squib meter and get the response.
		/// </summary>
		/// <param name="commandString">The command to send.</param>
		/// <returns>The squib meter response as string.</returns>

		private string IOQuery(string commandString)
		{
			// Flush squib meter buffer
			_serialPort.Write("FS");

			// clear our buffer
			Array.Clear(_readBuffer, 0, _readBuffer.Length);

			// send the data out
			_serialPort.Write(commandString);

			// read from the response
			int numBytesRead = _serialPort.Read(_readBuffer, 0, _readBuffer.Length);

			// convert response to a string
			string rspStr = ConvertToString(ref _readBuffer);

			// split out the command responce from the return data (divided by carriage return)
			string[] result = rspStr.Split(new string[] { "\n", "\r\n", "<CR>" }, StringSplitOptions.None);

			// Check command responce (0 = OK)
			if (result[0] == "1")
			{
				throw new Exception("Unknown Command");
			}
			if (result[0] == "2")
			{
				throw new Exception("Command now allowed in present mode");
			}

			return result[1];
		}

		/// <summary>
		/// Sends a serial Command to the instrument.
		/// </summary>
		/// <param name="commandString">The serial Command to be sent to the instrument.</param>
		private void IOWrite(string commandString)
		{
			// note each command sent has different return data/status/error formatting, IOWrite is just a direct write
			// IOQuery will do a read, and return all the data    

			// send command to serial port
			_serialPort.Write(commandString);
		}


		#endregion

		#region PublicFunctions

		/// <summary>
		/// DMMSquibMeter101SQBRAK factory constructor
		/// </summary>
		/// <param name="deviceName"></param>
		/// <param name="configurationManager"></param>
		public DMMSquibMeter101SQBRAK(string deviceName, IConfigurationManager configurationManager, ILogger logger)
		{
			Name = deviceName;

			_logger = logger;

			_configurationManager = configurationManager;
			_configuration = _configurationManager.GetConfiguration(Name);

			string comPortName = _configuration.GetConfigurationValue("DMMSquibMeter101SQBRAK", "ComPortName", "COM1");
			_address = comPortName;
			int baudRate = _configuration.GetConfigurationValue("DMMSquibMeter101SQBRAK", "BaudRate", 9600);
			Parity parity = _configuration.GetConfigurationValue("DMMSquibMeter101SQBRAK", "Parity", Parity.None);
			int dataBits = _configuration.GetConfigurationValue("DMMSquibMeter101SQBRAK", "DataBits", 8);
			StopBits stopBits = _configuration.GetConfigurationValue("DMMSquibMeter101SQBRAK", "StopBits", StopBits.One);

			_serialPort = new SerialPort(comPortName, baudRate, parity, dataBits, stopBits);
			_lastType = 0;
			_lastRange = 0;
			_lastResolution = 0;
			_readBuffer = new byte[1000];

			//created in Initialize()

			_state = State.Uninitialized;
			_selfTestResult = SelfTestResult.Unknown;
		}

		/// <summary>
		/// The constructor which opens the handle to the DMM and performs a self test on the instrument
		/// </summary>
		/// <param name="name">The name of this dmm</param>
		/// <param name="address">The address of the DMM</param>
		public DMMSquibMeter101SQBRAK(string name, string comPortName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
		{
			_name = name;
			_address = comPortName;
			_serialPort = new SerialPort(comPortName, baudRate, parity, dataBits, stopBits);
			_lastType = 0;
			_lastRange = 0;
			_lastResolution = 0;
			_readBuffer = new byte[1000];
			_logger = LogManager.GetCurrentClassLogger();

			//created in Initialize()

			_state = State.Uninitialized;
			_selfTestResult = SelfTestResult.Unknown;
		}

		
		/// <summary>
		/// The Finalizer which will release resources if required
		/// </summary>
		~DMMSquibMeter101SQBRAK()
		{
			Dispose(false);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool ClearErrors()
		{
			throw new NotImplementedException();  // if serial command for clearing errors add here, if not leave as is
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="type"></param>
		/// <param name="range"></param>
		/// <param name="resolution"></param>
		public void ConfigureCurrentMeasurement(MeasurementFunction type, Current range, Current resolution)
		{
			throw new NotImplementedException();  // if serial command for clearing errors add here, if not leave as is
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="type"></param>
		/// <param name="range"></param>
		/// <param name="resolution"></param>
		public void ConfigureFrequencyMeasurement(MeasurementFunction type, Frequency range, Frequency resolution)
		{
			throw new NotImplementedException();  // if serial command for clearing errors add here, if not leave as is
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="type"></param>
		/// <param name="range"></param>
		/// <param name="resolution"></param>
		public void ConfigureResistanceMeasurement(MeasurementFunction type, Resistance range, Resistance resolution)
		{
			if (type != MeasurementFunction.FourWireResistance && type != MeasurementFunction.TwoWireResistance)
			{
				throw new Exception("only FourWireResistance or TwoWireResistance is acceptable for param type: " + _lastType.ToString());
			}

			_lastType = type;
			_lastRange = range.Ohms;
			_lastResolution = resolution.Ohms;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="type"></param>
		/// <param name="range"></param>
		/// <param name="resolution"></param>
		public void ConfigureVoltageMeasurement(MeasurementFunction type, Voltage range, Voltage resolution)
		{
			throw new NotImplementedException();  // if serial command for clearing errors add here, if not leave as is
		}

		/// <summary>
		/// 
		/// </summary>
		public string DetailedStatus
		{
			get
			{
				versionInfo = IOQuery("VR");
				return "Squib Meter version Information (Cage Code | Model Number | Serial Number | Firmware Version | Calibration Date): " + versionInfo;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public bool DisplayEnabled
		{
			get
			{
				throw new NotImplementedException();
			}

			set
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// Dispose of this objects resources
		/// </summary>
		public void Dispose()
		{
			try
			{
				Dispose(true);

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

		/// <summary>
		/// 
		/// </summary>
		public bool FrontPanelEnabled
		{
			get
			{
				throw new NotImplementedException();
			}

			set
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public InstrumentMetadata Info
		{
			get
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void Initialize()
		{
			if (_state == State.Uninitialized)
			{
				// Open the port
				_serialPort.Open();

				// set the squib meter to remote state 
				IOQuery("RM");

				// Ensure continuous reading is off
				IOQuery("COFF");

				// Flush FIFO buffer
				IOQuery("FS");

				_state = State.Ready;
			}
			else
			{
				throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString());
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public MeasurementFunction MeasurementType
		{
			get
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public Current MeasureCurrent(int timeout)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public Frequency MeasureFrequency(int timeout)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public Resistance MeasureResistance(int timeout)
		{
			if (_lastType != MeasurementFunction.FourWireResistance && _lastType != MeasurementFunction.TwoWireResistance)
			{
				throw new Exception("only FourWireResistance or TwoWireResistance is acceptable for param type: " + _lastType.ToString());
			}

			IOQuery("SR"); //todo need to add in range values

			// send serial command to send & read response
			// set range
			// set resolution
			// any other settings that need to be sent (write) before taking measurement

			string response = IOQuery("RV");

			// parse the response //TODO Parse the return data (0= command ok, 1= unknown command, 2= command not allowed in present mode | reading | over range state | wiring error state | calibration state (ok or bad) | hardware state (ok or bad))
			double rsp = Util.ConvertStringToDouble(response); //if string is only number this is ok, if there are any other char must strip them off

			return Resistance.FromOhms(rsp);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public Voltage MeasureVoltage(int timeout)
		{
			throw new NotImplementedException();  // if serial command for clearing errors add here, if not leave as is
		}

		/// <summary>
		/// 
		/// </summary>
		public string Name
		{
			get { return _name; }
			set { _name = value; }
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public SelfTestResult PerformSelfTest()
		{
			int testResult = 0;
			const string BatteryState = "LOW";
			string result = IOQuery("RV");

			// Set measurement input to backplane after performing self test.  Default is front panel.
			// _dmm.Measurement.In = VTEXDmmInputSelectEnum.VTEXDmmInputSelectInternal;

			if (result == BatteryState)
			{
				_selfTestResult = Raytheon.Instruments.SelfTestResult.Fail;
				throw new Exception("Battery state: " + testResult + " Battery State Message: " + result);
			}

			_selfTestResult = Raytheon.Instruments.SelfTestResult.Pass;

			return _selfTestResult;
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reset()
		{
			IOWrite("RST");
		}

		/// <summary>
		/// 
		/// </summary>
		public SelfTestResult SelfTestResult
		{
			get
			{
				return _selfTestResult;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public State Status
		{
			get
			{
				return _state;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void Shutdown()
		{
			if (_state == State.Ready)
			{
				IOWrite("RST"); // send reset command
				IOWrite("LM"); // sets squib meter to local mode (it is set to remote in initilize)

				_serialPort.Close();

				_state = State.Uninitialized;
			}
		}

		#endregion
	}
}
