// 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: interface definition.
///
///	For details, please refer to the NI-VISA Programmer Reference Manual
/// (http://www.ni.com/pdf/manuals/370132c.pdf)

#pragma once

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

// Standard includes
#include <iterator>

// Personal includes
#include "IEEE488Instrument.h"
#include "VISAInstrumentTypes.h"
#include "SCPIInstrumentBuffer.h"
#include "Synchronization.h"

using namespace MTL::Synchronization;

namespace MTL {
	namespace Instrument {
		//----------------------------------------------------------------------//
		/// \brief Return user-readable description of the given status code.
		/// \param[in]	Session		VISA session.
		/// \param[in]	Status		Status code to be described.
		/// \return String with description of given status code.
		std::string StatusDescription(ViSession Session, ViStatus Status);

		//----------------------------------------------------------------------//
		/// \brief VISA Resource Manager class.
		class CVISAResourceManager: public CIEEE488ResourceManager
		{
		private:
			ViSession		m_DefaultRM;
			
		public:
			// Constructors / destructors
			CVISAResourceManager();				///< Constructor.
			virtual ~CVISAResourceManager();	///< Destructor.

			// Initialization / Info
			virtual bool Initialize();			///< Initialize the Resource Manager.
			ViStatus Status();					///< Return Resource Manager status.

			/// \brief Return description of status word.
			/// \param[in]	Status			Status word.
			/// \return Status description.
			virtual std::string StatusDescription (I32 Status);
			
			const ViSession & Session();		///< Return Resource Manager session identifier.
			virtual bool Timeout();				///< Last operation timed out

			// Resource utilities
			/// \brief Find VISA resources.
			/// \param[out]	rList			List of matching VISA resources.
			/// \param[in]	Filter			Grep expression to select resource names.
			/// \return True if successful.
			virtual bool FindResources(CResourceList & rList, std::string Filter = "?*");
			
			/// \brief Find VISA resources and parse the resource strings.
			/// \param[out]	rList			Parsed list of VISA resources.
			/// \param[in]	Filter			Grep expression to select resource names.
			/// \return True if successful.
			bool FindResources(CParsedResourceList & rList, std::string Filter = "?*");
			
			/// \brief Get the interface type and number for the given VISA resource name.
			/// \param[in]  rRsrc 			VISA resource name.
			/// \param[out] rIntfType		Resource type.
			/// \param[out] rIntfNumber		Resource number.
			/// \return True if successful.
			bool ResourceInfo(tResourceName & rRsrc, eInterfaceType & rIntfType, tInterfaceNumber & rIntfNumber);
			
			/// \brief Parse the given VISA resource name.
			/// \param[in]  rRsrc 			VISA resource name.
			/// \param[out] rReturnedInfo	Parsed resource information.
			/// \return True if successful.
			bool ResourceInfo(tResourceName & rRsrc, VISAResourceInfo & rReturnedInfo);
		};

		//----------------------------------------------------------------------//
        /// \brief VISA instrument class.
		class CVISAInstrument: public CIEEE488Instrument
		{
		private:
			ViSession					m_InstrSession;		///< Open session
            bool                        m_ExclusiveLock;    ///< Session has exclusive lock

		public:
			// Constructors / destructors
			/// \brief Constructor.
			/// \param[in]  rRM		VISA Resource Manager.
			/// \param[out] Rsrc	VISA resource name.
			CVISAInstrument(CVISAResourceManager & rRM, tResourceName Rsrc);
			virtual ~CVISAInstrument();		///< Destructor.

			// Connexion
			/// \brief Open a session to this VISA instrument.
			/// \return True if successful.
			virtual bool Open(void);

			/// \brief Open a session to this VISA instrument.
			/// \param[in]	AccessMode	Access mode (exclusive lock, no lock, load configuration).
			/// \param[out]	Timeout_ms	Timeout for operations during this session, in ms.
			/// \return True if successful.
			bool Open(eOpenAccessMode AccessMode, ViUInt32 Timeout_ms = 0);

			virtual void Close();			///< Close session to this VISA instrument.
			virtual bool IsOpen();			///< Check whether a session to this instrument is open.

			// Info
			/// \brief Return description of status word.
			/// \param[in]	Status			Status word.
			/// \return Status description.
			virtual std::string StatusDescription (I32 Status);
			
			const ViSession & Session();	///< Return instrument session identifier.
			virtual bool Timeout();			///< Last operation timed out

			// Write / Read
			/// \brief Write to a VISA instrument: C string variant.
			/// \param[in]	Str				Data to write.
			/// \return True if successful.
			virtual bool Write(const char * Str);
			
			/// \brief Write to a VISA instrument: C++ string variant.
			/// \param[in]	rStr			Data to write.
			/// \return True if successful.
			virtual bool Write(const std::string & rStr);

			/// \brief Write to a VISA instrument: SCPI buffer class variant.
			/// \param[in]	rBuf			Data to write.
			/// \return True if successful.
			virtual bool Write(const CSCPIBuffer & rBuf);

			/// \brief Write to a VISA instrument: VISA buffer variant.
			/// \param[in]	WriteBuf		Data to write.
			/// \param[in]	BuffLen			Number of bytes to write.
			/// \return True if successful.
			bool Write(const ViBuf WriteBuf, ViUInt32 BuffLen);

			/// \brief Read from a VISA instrument: SCPI buffer class variant.
			/// \param[in]	rBuf			Buffer to receive the data.
			/// \param[in]	Append			Whether to append to the existing data.
			/// \return True if successful.
			virtual bool Read(CSCPIBuffer & rBuf, bool Append = false);
			
			/// \brief Read from a VISA instrument: VISA buffer variant.
			/// \param[in]	ReadBuf			Buffer to receive the data.
			/// \param[in]	ReadLen			Number of bytes to read.
			/// \param[out]	rRetLen			Number of bytes actually read.
			/// \return True if successful.
			bool Read(ViPBuf ReadBuf, ViUInt32 ReadLen, ViUInt32 & rRetLen);

			// Other operations
			/// \brief	Set the timeout for this instrument session.
			/// \param[out]	Timeout			Timeout for operations during this session, in ms.
			/// \return True if successful.
			virtual bool SetTimeout(ViUInt32 Timeout);
			
			virtual bool Clear();			///< Clear the instrument.
			
			/// \brief Read status byte.
			/// \param[out]	rSTB			Status byte.
			/// \return True if successful.
			virtual bool ReadSTB(ViUInt16 & rSTB);
			
			/// \brief	Assert a trigger.
			/// \return True if successful.
			virtual bool AssertTrigger(void);
			
			/// \brief	Assert a trigger.
			/// \param[in]	Protocol		Trigger protocol (usually Default)
			/// \return True if successful.
			bool AssertTrigger(eTriggerProtocol Protocol);
			
			// Lock / Unlock
			/// \brief Obtain an exclusive lock for this session
			/// \param[in]	Timeout			Timeout value, in ms.
			/// \return True if successful.
			virtual bool LockExclusive(U32 Timeout);
			
			/// \brief Obtain a shared lock for this session.
			/// \param[in]	Timeout			Timeout value, in ms.
			/// \param[in]	RequestedKey	Request this key for sharing with other sessions; can be VI_NULL.
			/// \param[out]	AccessKey		Key for sharing with other sessions, returned by VISA; at least 256 bytes.
			/// \return True if successful.
			bool LockShared(ViUInt32 Timeout, ViKeyId RequestedKey, ViChar AccessKey[]);
			
            virtual bool Unlock();          ///< Unlock the session.

            virtual bool LockedExclusive(); ///< Check whether session is locked exclusively.

			// Events
			/// \brief Enable service requests
			/// \return True if successful.
			virtual bool EnableEvent (void);

			/// \brief Disable service requests
			/// \return True if successful.
			virtual bool DisableEvent (void);

			/// \brief Wait for a service request
			/// \param[in]	Timeout			Timeout value, in ms. No timeout if == 0.
			/// \return True if successful.
			virtual bool WaitOnEvent (U32 Timeout);

			/// \brief Discard service requests
			/// \return True if successful.
			virtual bool DiscardEvents (void);

			/// \brief Enable notification of a specified event.
			/// \param[in]	Type			Logical event identifier.
			/// \param[in]	Mechanism		How to handle the event.
			/// \return True if successful.
			bool EnableEvent(eEventType Type, eEventMechanism Mechanism);
			
			/// \brief Disable notification of a specified event.
			/// \param[in]	Type			Logical event identifier.
			/// \param[in]	Mechanism		How to handle the event.
			/// \return True if successful.
			bool DisableEvent(eEventType Type, eEventMechanism Mechanism);
			
			/// \brief Wait for a specified event.
			/// \param[in]	Type			Logical event identifier.
			/// \param[in]	Timeout			Timeout, in ms.
			/// \return True if successful.
			bool WaitOnEvent(eEventType Type, ViUInt32 Timeout);
			
			/// \brief Wait for all events.
			/// \param[in]	Timeout			Timeout, in ms.
			/// \return True if successful.
			bool WaitOnAllEvents(ViUInt32 Timeout);
			
			/// \brief Discard event occurrences for specified event types and mechanisms.
			/// \param[in]	Type			Logical event identifier.
			/// \param[in]	Mechanism		How to handle the event.
			/// \return True if successful.
			bool DiscardEvents(eEventType Type, eEventMechanism Mechanism);

			// Attributes
			/// \brief Retrieves the state of an attribute.
			/// \param[in]	Attribute		Attribute identifier (e.g. VI_ATTR_ASRL_PARITY).
			/// \param[out]	Value			Attribute value.
			/// \return True if successful.
			bool GetAttribute(ViAttr Attribute, void * Value);

			// USB
			/// \brief Performs a USB control pipe transfer from the instrument.
			/// \param[in]	bmRequestType	bmRequestType parameter of setup stage of a USB control transfer.
			/// \param[in]	bRequest		bRequest parameter of setup stage of a USB control transfer.
			/// \param[in]	wValue			wValue parameter of setup stage of a USB control transfer.
			/// \param[in]	wIndex			wIndex parameter of setup stage of a USB control transfer.
			/// \param[in]	wLength			wLength parameter of the setup stage of a USB control transfer.
			/// \param[out]	buf				Data buffer for optional data stage of the control transfer.
			/// \param[out]	rretCnt			Number of bytes actually transferred in optional data stage of control transfer.
			/// \return True if successful.
			bool UsbControlIn(ViInt16 bmRequestType, ViInt16 bRequest, ViUInt16 wValue, ViUInt16 wIndex, ViUInt16 wLength, ViPBuf buf, ViUInt16 & rretCnt);
			
			/// \brief Performs a USB control pipe transfer to the instrument.
			/// \param[in]	bmRequestType	bmRequestType parameter of setup stage of a USB control transfer.
			/// \param[in]	bRequest		bRequest parameter of setup stage of a USB control transfer.
			/// \param[in]	wValue			wValue parameter of setup stage of a USB control transfer.
			/// \param[in]	wIndex			wIndex parameter of setup stage of a USB control transfer.
			/// \param[in]	wLength			wLength parameter of the setup stage of a USB control transfer.
			/// \param[in]	buf				Data buffer to be sent during optional data stage of the control transfer.
			/// \return True if successful.
			bool UsbControlOut(ViInt16 bmRequestType, ViInt16 bRequest, ViUInt16 wValue, ViUInt16 wIndex, ViUInt16 wLength, ViBuf buf);

			// Serial
			/// \brief Set all the instrument attributes to configure the serial communication.
			/// \param[in]	rPortSettings	Serial port settings.
			/// \return True if successful.
			bool ConfigSerialPort(const sSerialPortSettings & rPortSettings);
		};

}}	// namespace MTL::Instrument
