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

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief USBTMC driver based on libusb: interface definition.
///

#pragma once

// Personal includes
#include <vector>
#include <list>
#include <string>
#include <queue>

#include "libusb.h"
#include "IEEE488Instrument.h"
#include "SCPIInstrumentBuffer.h"
#include "Synchronization.h"
#include "Exception.h"

using namespace MTL::Synchronization;

namespace MTL {
	namespace Instrument {
		
		//----------------------------------------------------------------------//
		/// \brief USBTMC Resource Manager class.
		class CUSBTMCResourceManager: public CIEEE488ResourceManager
		{
			friend class				CUSBTMCInstrument;
			
		private:
            typedef struct CUSBTMCDeviceListEntry
			{
				tResourceName			ResourceName;
				libusb_device *			pDevice;
				std::vector<U8>			PortPath;
				bool					PluggedIn;
				
				void clear(void)
				{
					ResourceName.clear();
					pDevice = nullptr;
					PortPath.clear();
					PluggedIn = false;
				}
			}							tUSBTMCDeviceListEntry;
			
			typedef std::list<tUSBTMCDeviceListEntry>
										tUSBTMCDeviceList;

		private:
			libusb_context *			m_pContext;
			tUSBTMCDeviceList			m_DeviceList;
			
		private:
			bool GetDeviceListEntry(const tResourceName ResourceName, libusb_device * & pDevice);
			MTL::CException<CUSBTMCResourceManager> Exception(I32 Status, std::string Location);

		public:
			// Constructors / destructors
            /// \brief Constructor
            CUSBTMCResourceManager(void);

            /// \brief Destructor
            virtual ~CUSBTMCResourceManager(void);

            // Initialize the Resource Manager
            virtual bool Initialize(void);

			// Resource utilities
			/// \brief Find USBTMC resources.
			/// \param[out]	rList			List of matching USBTMC resources.
			/// \param[in]	Filter			"VID:PID"; VID & PID are decimal integers.
			/// \return True if successful.
			virtual bool FindResources(CResourceList & rList, std::string Filter = "?*");

			/// \brief Return description of status word.
			/// \param[in]	Status			Status word.
			/// \return Status description.
			virtual std::string StatusDescription (I32 Status);
			
            /// \brief Last operation timed out
            virtual bool Timeout(void);

        }; // CUSBTMCResourceManager

        //----------------------------------------------------------------------//
        /// \brief USBTMC instrument class.
		class CUSBTMCInstrument: public CIEEE488Instrument
		{
		private:
			typedef CUSBTMCResourceManager::tUSBTMCDeviceListEntry	tUSBTMCDeviceListEntry;
			typedef CUSBTMCResourceManager::tUSBTMCDeviceList		tUSBTMCDeviceList;
            struct InterruptInData
            {
                U8                              bNotify1;
                U8                              bNotify2;
            };
            typedef struct InterruptInData      t_InterruptInData;

		private:
            libusb_device *                 m_pDevice;
            libusb_device_handle *          m_hDevice;
            U8                              m_ConfigurationNo;
            U8                              m_InterfaceNo;
            U8                              m_InterruptEndpoint;
            U8                              m_BulkInEndpoint;
            U8                              m_BulkOutEndpoint;
            I32                             m_BulkInMaxPacketSize;
            U8                              m_bTag;
            bool                            m_ExclusiveLock;
            bool                            m_SRQEnable;
            std::queue<t_InterruptInData>   m_SRQQueue;

		private:
			bool USBTMCWrite(U8 MessageID, const char * pData, const size_t Size, U8 TransferAttributes);
			bool USBTMCClearBulkIn(U8 bTag);
			bool USBTMCClearBulkOut(U8 bTag);
			MTL::CException<CUSBTMCInstrument> Exception(I32 Status, std::string Location);

		public:
            // Constructors / destructors
            /// \brief Constructor.
			/// \param[in]  rRM		USBTMC Resource Manager.
			/// \param[out] Rsrc	USBTMC resource name.
			CUSBTMCInstrument(CUSBTMCResourceManager & rRM, tResourceName Rsrc);

            /// \brief Destructor
            virtual ~CUSBTMCInstrument(void);	///< Destructor.

			// Connection
			/// \brief Open this USBTMC instrument.
			/// \return True if successful.
			virtual bool Open(void);

            /// \brief Close this VXI-11 instrument
            virtual void Close(void);

            /// \brief Check whether a session to this instrument is open
            virtual bool IsOpen(void);

            // Info
			/// \brief Return description of status word.
			/// \param[in]	Status			Status word.
			/// \return Status description.
			virtual std::string StatusDescription (I32 Status);
			
            /// \brief Last operation timed out
            virtual bool Timeout(void);

            // Write / Read
			/// \brief Write to a USBTMC instrument: C string variant.
			/// \param[in]	Str				Data to write.
			/// \return True if successful.
			virtual bool Write(const char * Str);
			
			/// \brief Write to a USBTMC 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 USBTMC instrument: SCPI buffer class variant.
			/// \param[in]	rBuf			Data to write.
			/// \return True if successful.
			virtual bool Write(const CSCPIBuffer & rBuf);

			/// \brief Read from a USBTMC 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 Clear the instrument
            /// \return True if successful.
            virtual bool Clear(void);

            /// \brief Read status byte.
			/// \param[out]	rSTB			Status byte.
			/// \return True if successful.
			virtual bool ReadSTB(U16 & rSTB);
			
			/// \brief	Assert a trigger.
			/// \return True if successful.
			virtual bool AssertTrigger(void);
			
			// 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 Unlock the session
            /// \return True if successful.
            virtual bool Unlock(void);

            /// \brief Check whether session is locked exclusively
            /// \return True if successful.
            virtual bool LockedExclusive(void);

            /// \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);

        }; // CUSBTMCInstrument

}}	// namespace MTL::Instrument
