//>>***************************************************************************
//                              UNCLASSIFIED
//
//                COPYRIGHT 2017 RAYTHEON MISSILE SYSTEMS 
//                         ALL RIGHTS RESERVED
//   This data was developed pursuant to Contract Number HQ0147-12-C-0004/1088370
//     with the US Government. The US Government's rights in and to this 
//        copyrighted data are as specified in DFAR 252.227-7013 
//               which was made part of the above contract.
//
//    Distribution Statement: D -Distribution authorized to the DoD and DoD 
//       contractors only based on Critical Technology Requirements, May 2001.
//       Other requests shall be referred to PEO THEATER AIR DEFENSE (PMS 422).
//    Warning: - This document contains technical data whose export is restricted
//       by the Arms Export Control Act (Title 22, U.S.C.) or Export 
//       Administration Act of 1979, as amended (Title 50, U.S.C.). Violations 
//       of these laws are subject to severe criminal penalties. Disseminate in 
//       accordance with provisions of DoD 5230.25.
//    Destruction Notice: - For unclassified, limited distribution information, 
//       destroy by any method that will prevent disclosure of contents or 
//       reconstruction of the document. Classified information, destroy in 
//       accordance with DoD-5220.22-M or OPNAVINST 5510.1h.
//>>***************************************************************************

using NLog;
using Raytheon.Common;
using System;
using System.Net.Sockets;
using System.Text;

namespace Raytheon.Instruments
{
   /// <summary>
   /// This is a class for controlling a VTI EX1200-3001, 5002, 4264 switch card.
   /// It only supports individual relay mode at this time.
   /// </summary>
   public class SwitchKeysightScpi : ISwitch
	{
		#region PrivateMemberVariables
		private const string READ_ERROR_STATUS_CMD = "SYST:ERR?";
		private const string _SELFTESTCMD = "*TST?";
		private const string _RESETCMD = "*RST";
		//@@@ add scpi commands here

		private int _PORT = 5025;
		private const int _READ_TIMEOUT = 5000;
		private byte[] _readBuffer;
		private NetworkStream _tcpStream;
		private string _name;

		/// <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 object's resources.
		/// </summary>
		/// <param name="disposing">True = currently disposing, False = not disposing.</param>
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				if (disposing && _tcpStream != null)
				{
				}
			}
			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>
		/// Convert scpi data to string
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private string ConvertToString(ref byte[] data)
		{
			string rsp = System.Text.Encoding.ASCII.GetString(data);
			return rsp;
		}

		/// <summary>
		/// Get the error code.
		/// </summary>
		/// <returns>The error code (number).</returns>
		private int GetErrorCode()
		{
			// not calling IOQuery() here so IOQuery() can call GetErrorCode() after each query
			string command = READ_ERROR_STATUS_CMD + "\n";

			// convert to a byte array
			byte[] commandBuffer = Encoding.ASCII.GetBytes(command);

			// send the data out
			_tcpStream.Write(commandBuffer, 0, commandBuffer.Length);

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

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

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

			// parse the response
			string[] tokens = rspStr.Split(',');

			int ret = Util.ConvertStringToInt32(tokens[0]);

			return ret;
		}


		/// <summary>
		/// Send a command to the DMM and get the response
		/// </summary>
		/// <param name="commandString">The command to send</param>
		/// <returns>The DMM response</returns>
		private string IOQuery(string commandString)
		{
			// send the command
			IOWrite(commandString);

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

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

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

			// check for errors
			int err = GetErrorCode();
			if (err != 0)
			{
				throw new Exception("SwitchKeysightScpi:IOQuery() - returned error code: " + err.ToString());
			}

			return rspStr;
		}

		/// <summary>
		/// Sends a SCPI Command to the instrument.
		/// </summary>
		/// <param name="commandString">The SCPI Command to be sent to the instrument.</param>
		private void IOWrite(string commandString)
		{
			// convert to a byte array
			byte[] commandBuffer = Encoding.ASCII.GetBytes(commandString);

			// send the data out
			_tcpStream.Write(commandBuffer, 0, commandBuffer.Length);

			// check for errors
			int err = GetErrorCode();
			if (err != 0)
			{
				throw new Exception("SwitchKeysightScpi:IOQuery() - returned error code: " + err.ToString());
			}
		}

		/// <summary>
		/// Perform self-test.
		/// </summary>
		private void SelfTest()
		{
			try
			{
				// change the timeout to account for the long self test
				_tcpStream.ReadTimeout = 30000;

				// send the command and get the response
				string command = _SELFTESTCMD + "\n";
				string rspStr = IOQuery(command);

				// parse the response
				string[] tokens = rspStr.Split('\n');

				int rsp = Util.ConvertStringToInt32(tokens[0]);

				if (rsp != 0)
				{
					string errorMsg = "returned an error: " + rsp.ToString() + "," + rspStr;
					throw new Exception(errorMsg);
				}
			}
			catch (Exception)
			{
				throw;
			}
			finally
			{
				// restore the timeout
				_tcpStream.ReadTimeout = _READ_TIMEOUT;
			}
		}
		#endregion

		#region PublicFunctions

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

			_logger = logger;

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

			var address = _configuration.GetConfigurationValue("SwitchKeysightScpi", "Address", "127.0.0.1");
			_PORT = _configuration.GetConfigurationValue("SwitchKeysightScpi", "Port", 5025);

			const int READ_BUFFER_SIZE = 512;

			_readBuffer = new byte[READ_BUFFER_SIZE];

			TcpClient dmmSocketConn = new TcpClient(address, _PORT);
			_tcpStream = dmmSocketConn.GetStream();
			_tcpStream.ReadTimeout = _READ_TIMEOUT;

			SelfTest();
		}

		/// <summary>
		/// Constructor for RelaySwitch card.
		/// </summary>
		/// <param name="address">The address of the RelaySwitch.</param>
		/// <param name="options">The options used for setting up the instrument.</param>
		public SwitchKeysightScpi(string name, string address)
		{
			const int READ_BUFFER_SIZE = 512;

			_name = name;

			_logger = LogManager.GetCurrentClassLogger();

			_readBuffer = new byte[READ_BUFFER_SIZE];

			TcpClient dmmSocketConn = new TcpClient(address, _PORT);
			_tcpStream = dmmSocketConn.GetStream();
			_tcpStream.ReadTimeout = _READ_TIMEOUT;

			SelfTest();
		}

		/// <summary>
		/// Finalizer.
		/// </summary>
		~SwitchKeysightScpi()
		{
			Dispose(false);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool ClearErrors()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Close a single relay.
		/// </summary>
		/// <param name="relay">The relay to close.</param>
		public void Connect(string path)
		{
			// send the command

			//IOWrite(command);

			//read back to verify state of relay?


			//"ROUT:CLOS (@3923)"
		}

		/// <summary>
		/// Open a single relay.
		/// </summary>
		/// <param name="relay">The relay to open.</param>
		public void Disconnect(string path)
		{
			// send the command

			//IOWrite(command);

			//read back to verify state of relay?
			//"ROUT:OPEN (@3923)")
		}

		/// <summary>
		/// 
		/// </summary>
		public void DisconnectAll()
		{
		}

		/// <summary>
		/// Dispose of 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 string DetailedStatus
		{
			get
			{
				return "This is a Keysight 34980A switch";
			}
		}

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

			set
			{
				throw new NotSupportedException();
			}
		}

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

			set
			{
				throw new NotSupportedException();
			}
		}

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

		/// <summary>
		/// 
		/// </summary>
		public void Initialize()
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// 
		/// </summary>
		public bool IsDebounced
		{
			get
			{
				throw new NotSupportedException();
			}
		}

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

		/// <summary>
		/// 
		/// </summary>
		public SelfTestResult SelfTestResult
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public State Status
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public SelfTestResult PerformSelfTest()
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reset()
		{
			IOWrite(_RESETCMD + "\n");
		}

		/// <summary>
		/// 
		/// </summary>
		public void Shutdown()
		{
			if (_tcpStream != null)
			{
				Reset();
				_tcpStream.Dispose();
			}
		}
		#endregion
	}
}
