// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief C++ wrapper for NI-VISA: type definitions.

#pragma once

// External tools
#include "../resources/visa.h"

// Standard libraries
#include <string>
#include <vector>
#include <iostream>

namespace MTL {
	namespace Instrument
	{
		//----------------------------------------------------------------------//
		//								Types									//
		//----------------------------------------------------------------------//
		/// \brief VISA interface types.
		enum class eInterfaceType {
			GPIB		= VI_INTF_GPIB,					///< GPIB (a.k.a. HPIB or IEEE 488.1).
			VXI			= VI_INTF_VXI,					///< VXI (IEEE 488.2 over Ethernet).
			GPIB_VXI	= VI_INTF_GPIB_VXI,				///< Ethernet GPIB adapter. (?)
			ASRL		= VI_INTF_ASRL,					///< Serial.
			PXI			= VI_INTF_PXI,					///< PXI (PCI eXtensions for Instrumentation).
			TCPIP		= VI_INTF_TCPIP,				///< TCP/IP.
			USB			= VI_INTF_USB					///< USB.
		};

		typedef ViUInt16		tInterfaceNumber;		///< Interface number.

		/// \brief Classes of VISA resource.
		enum class eResourceClass {
			INSTR = 0,									///< Instrument.
			MEMACC,										///< Memory access.
			INTFC,										///< GPIB Bus Interface.
			BACKPLANE,									///< VXIbus Mainframe Backplane
			SERVANT,									///< Device-side functionality.
			SOCKET,										///< Ethernet Socket.
			RAW											///< USB Raw.
		};

		/// \brief Access modes for VISA resources
		enum class eOpenAccessMode {
			NoLock			= VI_NO_LOCK,				///< Open session without using an exclusive lock or loading configuration information
			ExclusiveLock	= VI_EXCLUSIVE_LOCK,		///< Acquire an exclusive lock immediately upon opening a session. If a lock cannot be acquired, the session is closed and an error is returned
			LoadConfig		= VI_LOAD_CONFIG			///< Configure attributes to values specified by an external configuration utility.
		};

		/// \brief Event mechanisms.
		enum class eEventMechanism {
			Queue				= VI_QUEUE,				///< Enable the session to queue events
			Handle				= VI_HNDLR,				///< Enable the session to invoke a callback function
			Suspendedhandler	= VI_SUSPEND_HNDLR,		///< Enable the session to execute the handler
			All					= VI_ALL_MECH			///< Enable the session to all events
		};

		/// \brief Event types.
		enum class eEventType : unsigned long {
			ServiceRequest		= VI_EVENT_SERVICE_REQ,			///< Service request was received from device or interface.
			Trig				= VI_EVENT_TRIG,				///< Trigger interrupt was received from the device.
			Clear				= VI_EVENT_CLEAR,				///< Device clear message was sent to the local controller.
			IOCompletion		= VI_EVENT_IO_COMPLETION,		///< Asynchronous operation has completed.
			Exception			= VI_EVENT_EXCEPTION,			///< Error condition has occured during an operation.
			GPIB_CIC			= VI_EVENT_GPIB_CIC,			///< GPIB controller has gained or lost CIC (controller-in-charge) status.
			GPIB_TALK			= VI_EVENT_GPIB_TALK,			///< GPIB controller was addressed to talk.
			GPIB_LISTEN			= VI_EVENT_GPIB_LISTEN,			///< GPIB controller has been addressed to listen.
			VXI_VME_SYSFAIL		= VI_EVENT_VXI_VME_SYSFAIL,		///< VXI/VME SYSFAIL* line was asserted.
			VXI_VME_SYSRESET	= VI_EVENT_VXI_VME_SYSRESET,	///< VXI/VME SYSRESET* line was asserted.
			VXI_SIGP			= VI_EVENT_VXI_SIGP,			///< VXIbus signal or VXIbus interrupt was received from the device.
			VXI_VME_INTR		= VI_EVENT_VXI_VME_INTR,		///< VXIbus interrupt was received from the device.
			PXI_INTR			= VI_EVENT_PXI_INTR,			///< PXI interrupt occurred.
			TCPIP_CONNECT		= VI_EVENT_TCPIP_CONNECT,		///< TCP/IP connection has been made.
			USB_INTR			= VI_EVENT_USB_INTR,			///< USB interrupt has occurred.
			ALL					= VI_ALL_ENABLED_EVENTS			///< All enabled events.
		};

		/// \brief Trigger protocols.
		///
		/// - Software Triggers for 488.2 Instruments (GPIB, VXI, TCPIP, and USB)\n
		/// 	For software triggers, VI_TRIG_PROT_DEFAULT is the only valid protocol.
		///	- For a GPIB device, VISA addresses the device to listen and then sends the GPIB GET command.
		///	- For a VXI device, VISA sends the Word Serial Trigger command.
		///	- For a USB device, VISA sends the TRIGGER message ID on the Bulk-OUT pipe.
		///	- Software Triggers for Non-488.2 Instruments (Serial INSTR, TCPIP SOCKET, and USB RAW)\n
		///		If VI_ATTR_IO_PROT is VI_PROT_4882_STRS, this operations sends "*TRG\n" to the device; otherwise, this
		///		operation is not valid. For software triggers, VI_TRIG_PROT_DEFAULT is the only valid protocol.
		///	- Hardware Triggering for VXI\n
		///		For hardware triggers to VXI instruments, VI_ATTR_TRIG_ID must first be set to the desired trigger line
		///		to use; this operation performs the specified trigger operation on the previously selected trigger line.
		///		For VXI hardware triggers, VI_TRIG_PROT_DEFAULT is equivalent to VI_TRIG_PROT_SYNC.
		///	- Trigger Reservation for PXI\n
		///		For PXI instruments, this operation reserves or releases (unreserves) a trigger line for use in external
		///		triggering. For PXI triggers, VI_TRIG_PROT_RESERVE and VI_TRIG_PROT_UNRESERVE are the only valid protocols.
		///
		/// *Reference: online documentation of viAssertTrigger
		///		(http://zone.ni.com/reference/en-XX/help/370131S-01/ni-visa/viasserttrigger/)*
		enum class eTriggerProtocol {
			Default			= VI_TRIG_PROT_DEFAULT,				///< Default protocol.
			VXI_Default		= VI_TRIG_PROT_DEFAULT,				///< Default protocol for VXI.
			VXI_On			= VI_TRIG_PROT_ON,					///< Asserts the trigger.
			VXI_Off			= VI_TRIG_PROT_OFF,					///< Deasserts the trigger.
			VXI_Sync		= VI_TRIG_PROT_SYNC,				///< Pulses the trigger (assert followed by deassert).
			PXI_Reserve		= VI_TRIG_PROT_RESERVE,				///< Reserve a trigger line.
			PXI_Unreserve	= VI_TRIG_PROT_UNRESERVE			///< Release a trigger line.
		};

		/// \brief Serial baudrate.
		enum class eSerialBaudrate {
			k110 = 110,			///< 110 baud
			k300 = 300,			///< 300 baud
			k600 = 600,			///< 600 baud
			k1200 = 1200,		///< 1200 baud
			k2400 = 2400,		///< 2400 baud
			k4800 = 4800,		///< 4800 baud
			k9600 = 9600,		///< 9600 baud
			k14400 = 14400,		///< 14400 baud
			k19200 = 19200,		///< 19200 baud
			k28800 = 28800,		///< 28800 baud
			k38400 = 38400,		///< 38400 baud
			k57600 = 57600,		///< 57600 baud
			k115200 = 115200,	///< 115200 baud
			k230400 = 230400,	///< 230400 baud
			k460800 = 460800,	///< 460800 baud
			k921600 = 921600	///< 921600 baud
		};
        /// \brief Number of data bits for the serial communication.
		enum class eSerialDataBits {
			k7 = 7,				///< 7 data bits
			k8 = 8				///< 8 data bits
		};									///< Number of data bits for the serial communication.
		/// \brief Parity configuration for the serial communication.
		enum class eSerialParity {
			kNone	= VI_ASRL_PAR_NONE,		///< Set Parity to None
			kOdd	= VI_ASRL_PAR_ODD,		///< Set Parity to Odd
			kEven	= VI_ASRL_PAR_EVEN,		///< Set Parity to Even
			kMark	= VI_ASRL_PAR_MARK,		///< Set Parity to Mark
			kSpace	= VI_ASRL_PAR_SPACE		///< Set Parity to Space

		};
		/// \brief Stop bit configuration for the serial communication.
		enum class eSerialStopBits {
			k1		= VI_ASRL_STOP_ONE,		///< Set Stop bit to 1
			k1_5	= VI_ASRL_STOP_ONE5,	///< Set Stop bit to 1.5
			k2		= VI_ASRL_STOP_TWO		///< Set Stop bit to 2
		};
		/// \brief Handshake configuration for the serial communication.
		enum class eSerialHandshake {
			kNone			= VI_ASRL_FLOW_NONE,								///< Set None
			kXonXoff		= VI_ASRL_FLOW_XON_XOFF,							///< Set Software Handshake Xon/Xoff
			kHardware		= VI_ASRL_FLOW_RTS_CTS,								///< Set Hardware Handshake RTS/CTS
			kHardAndSoft	= (VI_ASRL_FLOW_XON_XOFF | VI_ASRL_FLOW_RTS_CTS)	///< Set Hardware & Software Handshake (Xon/Xoff & RTS/CTS)
		};
        /// \brief Serial termination mode.
		enum class eSerialTermMode {
			kEndNone = VI_ASRL_END_NONE,			///< Set termination character to None
			kEndLast = VI_ASRL_END_LAST_BIT,		///< Set termination character to Last bit
			kEndTermChar = VI_ASRL_END_TERMCHAR,	///< Set termination character to the one specified by VI_ATTR_TERMCHAR()
			kEndBreak = VI_ASRL_END_BREAK			///< Set termination character to Break (Only available in Write termination)
		};
        /// \brief Serial port settings.
		struct sSerialPortSettings {
			std::string			COMPort;			///< [-]	String identifying the serial port to connect to
			eSerialBaudrate		Baudrate;			///< [bd]	Speed of the serial communication
			eSerialDataBits		DataBits;			///< [-]	Number of transmitted bits per packet
			eSerialParity		Parity;				///< [-]	Parity configuration of the serial communication
			eSerialStopBits		StopBits;			///< [-]	Stop bit configuration of the serial communication
			eSerialHandshake	Handshake;			///< [-]	Handshake configuration of the serial communication
			eSerialTermMode		ReadTermMode;		///< [-]	Termination Read mode of the serial communication
			char				ReadTermChar;		///< [-]	Specific termination character when readTermMode = kEndTermChar
			sSerialPortSettings()
				: COMPort(""),
				Baudrate(eSerialBaudrate::k9600),
				DataBits(eSerialDataBits::k8),
				Parity(eSerialParity::kNone),
				StopBits(eSerialStopBits::k1),
				Handshake(eSerialHandshake::kNone), 
				ReadTermMode(eSerialTermMode::kEndTermChar),
				ReadTermChar('\n')
			{}										///< Constructor.
		};

		// For future use
        /// \brief Information about a VISA resource.
		class VISAResourceInfo
		{
		public:
			typedef std::string		tClass;				///< Class: INSTR / RAW / ...
			typedef std::string		tExpandedName;		///< Full resource name.
			typedef std::string		tAlias;				///< Alias, if any.

		private:
			eInterfaceType		m_Type;
			tInterfaceNumber	m_Number;
			tClass				m_Class;
			tExpandedName		m_ExpName;
			tAlias				m_Alias;
		public:
			VISAResourceInfo(eInterfaceType Type = eInterfaceType::GPIB, tInterfaceNumber Number = 0, tClass Class = "", tExpandedName Expname = "", tAlias Alias = "")
				: m_Type(Type), m_Number(Number), m_Class(Class), m_ExpName(Expname), m_Alias(Alias)
			{}											///< Constructor.
			const eInterfaceType & Type() const
			{
				return m_Type;
			}											///< Return interface type.
			const tInterfaceNumber & Number() const
			{
				return m_Number;
			}											///< Return interface number.
			const tClass & Class() const
			{
				return m_Class;
			}											///< Return interface class.
			const tExpandedName & ExpName() const
			{
				return m_ExpName;
			}											///< Return full resource name.
			const tAlias & Alias() const
			{
				return m_Alias;
			}											///< Return alias.
		};

        /// \brief Parsed information about a VISA resource.
		struct sParsedResource
		{
			tResourceName		Name;					///< VISA resource name.
			VISAResourceInfo	Info;					///< Parsed VISA resource information.

			sParsedResource(tResourceName & rName, VISAResourceInfo & rInfo)
				: Name(rName), Info(rInfo)
			{}											///< Constructor.

			bool operator==(sParsedResource const & other) const
			{
				return (Name == other.Name);
			}											///< Equality operator.
			bool operator!=(sParsedResource const & other) const

			{
				return !(*this == other);
			}											///< Inequality operator.
		};
			
        /// \brief Parsed resource information for a list of instruments.
		class CParsedResourceList : public std::vector<sParsedResource>
		{
		public:
			bool operator==(CParsedResourceList const & other)
			{
				if (other.size() != size())
					return false;
				for (CParsedResourceList::const_iterator thisit = begin(), otherit = other.begin(); thisit != end(); thisit++, otherit++)
				{
					if (*thisit != *otherit)
						return false;
				}
				return true;
			}											///< Equality operator.
			bool operator!=(CParsedResourceList const & other)
			{
				if (other.size() != size())
					return true;
				for (CParsedResourceList::const_iterator thisit = begin(), otherit = other.begin(); thisit != end(); thisit++, otherit++)
				{
					if (*thisit != *otherit)
						return true;
				}
				return false;
			}											///< Inequality operator.
		};

}}	// namespace MTL::Instrument
