// VPlibusb.cpp : Defines the exported functions for the DLL application.
//

#include "VPusbIO.h"
//#include "PTTypeDefs.h"
#ifndef _WINDOWS_
#include "../VPlibusbux/Debugux.h"
#else
#include "Debug.h"
#endif
#include <sstream>
#include <chrono>
#include <thread>
#pragma warning (disable:4200)
#ifdef _VPUX_
//#include "/usr/include/libusb-1.0/libusb.h"
#include "libusb.h"
#else
#include "libusb.h"
#endif
#define MYCTX ((libusb_context*)m_pctx)
#define MYCTX_PTR ((libusb_context**)&m_pctx)
#define MYHND ((libusb_device_handle*)m_pdevhnd)
#define MYHND_PTR ((libusb_device_handle**)&m_pdevhnd)

#define MY_AR_XFER ((libusb_transfer *)m_parxfer)

using namespace iodbg;

PiDebug g_dbg;

#ifndef CLAMP
#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
#endif
typedef struct _HPINFO {
	uint32_t bfDig_IO : 8;
	uint32_t bfSrcLock : 8;
	uint32_t bfReserved : 16;
} HPINFO; 
typedef struct _SEUPNO {
	uint32_t seuid;
	uint32_t frame;
	HPINFO   HPinfo;		// 4 bytes
	uint32_t sensorCount;
} SEUPNO, *LPSEUPNO;
typedef struct _SEUPNO_HDR
{
	uint32_t preamble;
	uint32_t size;
	SEUPNO seupno;		//16 bytes
}SEUPNO_HDR;


//start up procedure
// initialize libusb
// discover vid:pid
//   if starting async read thread:
//		allocate async read transfer
//		launch async read thread (thproc_Cont)
//			thproc_cont calls libusb_handle_events over & over
//			when event occurs on transfer, MCB callback is called by libusb, from inside libusb_handle_events
//				the event may be a timeout or it may be new incoming data.
//				either way, MCB will resubmit the transfer request to capture the next incoming data
//
//shutdown procedure
// if async read thread is running:
//		cancel async read transfer
//		clear flag that keeps thproc_cont looping
//		close libusb context
//			meanwhile:
//			MCB will handle the cancel event by not resubmitting transfer request
//			handled event will cause libusb_handle_event to return in thproc_cont
//				thproc_cont will see that flag is now cleared and will:
//					free the async read transfer allocated at startup
//					exit
//			if, for some reason there was no active transfer:
//					the close operation will cause libusb_handle_event to return and the thproc_cont will exit


CVPusbIO::CVPusbIO(void) : 
	m_pctx(0)
	, m_pdevhnd(0)
	, m_parxfer(0)
	, m_iReadCmdTO(VPUSB_DEFAULT_CMD_TIMEOUT_MS)
	, m_iWriteCmdTO(VPUSB_DEFAULT_CMD_TIMEOUT_MS)
	, m_pthCont(0)
	, m_bContReadRunning(false)
	, m_iNextHostIndex(0)
	, m_iHostFC(0)
	, m_iLastBytesIn(0)
	, m_pLastBufIn(0)
	, m_pDataReadyCallback(0)
	, m_pCBInfo(0)
	//, m_bReadCmdTOIsError(false)
	//, m_iAsyncReadTOMS(VPUSB_DEFAULT_ASYNC_READ_TIMEOUT_MS)

{
	int r;
	if ( (r = libusb_init(MYCTX_PTR)) )
	{
	}
	else
	{
		libusb_set_debug(MYCTX, LIBUSB_LOG_LEVEL_WARNING);
	}

#ifdef _DEBUG
	SetTrace(true, PiDebug::PID_7_VERBOSE);
#else
	SetTrace(false);
#endif	
}

CVPusbIO::~CVPusbIO()
{
	if (Initialized())
	{
		//ReadContStop(); // The close operation will stop the cont loop.  Inadviseable to do that here.
		Close();

		libusb_exit(MYCTX);
	}

	m_pctx = 0;
}
void CVPusbIO::SetTrace(bool btrace, int ilev)
{
	//DOUT(__FUNCTION__ " : btrace=%s , ilev = %d\r\n", (btrace ? "true":"false"), ilev);

	if (btrace)
	{
		PiDebug::PIDSetLevel(ilev); //so normal i/o won't be hexdumped unless codetrace selected.
		if (ilev > PiDebug::PID_5_CODETRACE)
		{
			//DOUT0(__FUNCTION__ " : %d : Setting libusb debug level LIBUSB_LOG_LEVEL_DEBUG\r\n", __LINE__);
			libusb_set_debug(MYCTX, LIBUSB_LOG_LEVEL_DEBUG);
		}
		else
		{
			//DOUT0(__FUNCTION__ " : %d : Setting libusb debug level LIBUSB_LOG_LEVEL_WARNING\r\n", __LINE__);
			libusb_set_debug(MYCTX, LIBUSB_LOG_LEVEL_WARNING);
		}

	}
	else
	{
		//DOUT0(__FUNCTION__ " : %d : Setting libusb debug level LIBUSB_LOG_LEVEL_WARNING\r\n", __LINE__);
		PiDebug::PIDSetLevel(0);
		libusb_set_debug(MYCTX, LIBUSB_LOG_LEVEL_WARNING);
	}
}

int CreateVidPidList(libusb_context* pctx, libusb_device ** & devlist, uint16_t vid, uint16_t pid, vp_usbdevinfo arrDevInfo[], size_t & arrcount)
{
	CODETRACE;
	int r = 0;

	ssize_t devcount = libusb_get_device_list(pctx, &devlist);
	if (devcount < 0)
		return (int)devcount; // returns error code < 0

	//struct libusb_device *found = NULL;
	//struct libusb_device_handle *found_dev_handle = NULL;
	libusb_device *dev;
	int iFoundCount = 0;

	int iFoundArrIndex = -1;
	int i = 0;
	while ((dev = devlist[i]) != NULL)
	{
		struct libusb_device_descriptor desc;
		r = libusb_get_device_descriptor(dev, &desc);
		if (r < 0)
			break;

		if (desc.idVendor == vid && desc.idProduct == pid)
		{
			iFoundCount++; iFoundArrIndex++;
			// populate the array if size permits
			if (iFoundCount <= (int)arrcount)
			{
				vp_usbdevinfo * p = &arrDevInfo[iFoundArrIndex];
				p->usbDevIndex = i;
				p->usbBusNum = libusb_get_bus_number(dev);
				p->usbDevNum = libusb_get_device_address(dev);
				p->usbVID = vid; p->usbPID = pid;
			}
		}

		i++;
	}

	arrcount = iFoundCount;

	CODETRACEEX;
	return r;
}
// in: arrDevInfo[]
// in: arrcount indicates size of arrDevInfo
// out: arrDevInfo[] filled with info about devices with matching vid+pid, up to input arrcount
// out: arrcount indicates number of devices with matching vid+pid were found, this may be larger than array size
int CVPusbIO::ListVidPid(uint16_t vid, uint16_t pid, vp_usbdevinfo arrDevInfo[], size_t & arrcount)
{
	CODETRACE;
	int r = 0;
	if ((!arrDevInfo) || (arrcount == 0))
		return -1;

	libusb_device **devlist;
	ssize_t devcount = libusb_get_device_list(MYCTX, &devlist);
	if (devcount < 0)
		return (int)devcount; // returns error code

	//struct libusb_device *found = NULL;
	//struct libusb_device_handle *found_dev_handle = NULL;
	libusb_device *dev;
	int iFoundCount = 0;

	int iFoundArrIndex = -1;
	int i = 0;
	while ((dev = devlist[i]) != NULL)
	{
		struct libusb_device_descriptor desc;
		r = libusb_get_device_descriptor(dev, &desc);
		if (r < 0)
			break;

		if (desc.idVendor == vid && desc.idProduct == pid)
		{
			iFoundCount++; iFoundArrIndex++;
			// populate the array if size permits
			if (iFoundCount <= (int)arrcount)
			{
				vp_usbdevinfo * p = &arrDevInfo[iFoundArrIndex];
				p->usbDevIndex = i;
				p->usbBusNum = libusb_get_bus_number(dev);
				p->usbDevNum = libusb_get_device_address(dev);
				p->usbVID = vid; p->usbPID = pid;
			}
		}

		i++;
	}

	arrcount = iFoundCount;

	libusb_free_device_list(devlist, 1);

	CODETRACEEX;
	return r;
}

void find_endpoints(libusb_config_descriptor *conf_desc, int iface, uint8_t & ep_in, uint8_t & ep_out, uint16_t & out_pktsize)
{
	for (int j = 0; j < conf_desc->interface[iface].num_altsetting; j++)
	{
		for (int k = 0; k < conf_desc->interface[iface].altsetting[j].bNumEndpoints; k++)
		{
			const struct libusb_endpoint_descriptor *p_ep;
			p_ep = &conf_desc->interface[iface].altsetting[j].endpoint[k];

			if ((p_ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT))
			{
				if (p_ep->bEndpointAddress & LIBUSB_ENDPOINT_IN)
				{
					if (!ep_in)
						ep_in = p_ep->bEndpointAddress;
				}
				else
				{
					if (!ep_out)
					{
						ep_out = p_ep->bEndpointAddress;
						out_pktsize = p_ep->wMaxPacketSize;
					}
				}
			}

		}
	}
}

int CVPusbIO::DiscoverVidPid(uint16_t vid, uint16_t pid)
{
	CODETRACE;

	int r = 0;
	
	if (!Initialized())
		return -1;

	if (IsOpen())
		Close();

	vp_usbdevinfo arrDevInfo[VPUSB_MAX_DISCOVERABLE];
	size_t arrcount = VPUSB_MAX_DISCOVERABLE;
	libusb_device **devlist;

	if ((r = CreateVidPidList(MYCTX, devlist, vid, pid, arrDevInfo, arrcount)) < 0)
		return r;

	for (int d = 0; d < (int)arrcount; d++)
	{
		libusb_device * dev = devlist[ arrDevInfo[d].usbDevIndex ];
		libusb_device_handle * handle = 0;
		libusb_config_descriptor *conf_desc = 0;
		uint8_t nb_ifaces = 0, claimed_ifaces = 0;
		uint8_t ep_in = 0, ep_out = 0;
		uint16_t out_pktsize = 0;

		if ((r = libusb_open(dev, &handle)))
		{ }
		else if ((r = libusb_get_config_descriptor(dev, 0, &conf_desc)))
		{
			libusb_close(handle);
		}
		else
		{
			nb_ifaces = conf_desc->bNumInterfaces;
			claimed_ifaces = 0;
			for (uint8_t i = 0; (i < nb_ifaces) && (r==0); i++)
			{
				if ((r = libusb_claim_interface(handle, (int)i)))
				{}
				else
				{
					claimed_ifaces++;

					find_endpoints(conf_desc, (int)i, ep_in, ep_out, out_pktsize);
				}
			}

			if (claimed_ifaces == nb_ifaces)
			{
				m_pdevhnd = (void*)handle;
				m_devinfo = arrDevInfo[d];
				m_devinfo.usbNumInterfaces = nb_ifaces;
				m_devinfo.ep_in = ep_in;
				m_devinfo.ep_out = ep_out;
				m_devinfo.epout_maxPktsize = out_pktsize;

				break;
			}
		}

		// clean up failed attempt before trying the next one.
		if (claimed_ifaces != nb_ifaces)
		{
			for (int k = 0; k < nb_ifaces; k++)
				libusb_release_interface(handle, k);
		}
		if (handle)
			libusb_close(handle);

		libusb_free_config_descriptor(conf_desc);
	}

	libusb_free_device_list(devlist, 1);

	if (!IsOpen())
		r = -99;

	CODETRACEEX;

	return r;
}

#ifndef errinfo
typedef struct 
{
	int err;
	char * szerr;
}errinfo;
#endif

errinfo g_errinfo[E_VPUSB_ERR_MAX + 1] =
{ E_VPUSB_ERR_MAX, "E_VPUSB_ERR_MAX" };
//E_VPUSB_ERR_SUCCESS = 0
//, E_VPUSB_ERR_WRITE_COUNT_WRONG
//, E_VPUSB_ERR_MAX

void VPUSB_InitErrInfo()
{
	if (g_errinfo[0].err != E_VPUSB_ERR_MAX)
		return;

	for (int i = 0; i <= E_VPUSB_ERR_MAX; i++)
	{
		switch (i)
		{
		default:
			g_errinfo[i] = { E_VPUSB_ERR_MAX, "VPUSB_ERR NOT DEFINED" };
			break;
		case E_VPUSB_ERR_SUCCESS: // = 0           /**< 0, No error occured. */
			g_errinfo[i] = { E_VPUSB_ERR_SUCCESS, "E_VPUSB_ERR_SUCCESS" };
			break;
		case E_VPUSB_ERR_WRITE_COUNT_WRONG: //             /**< 1, libusb did not write number expected. */
			g_errinfo[i] = { E_VPUSB_ERR_WRITE_COUNT_WRONG, "E_VPUSB_ERR_WRITE_COUNT_WRONG" };
			break;
			//case E_DEVIOERR_MAX

		}
	}
}

const char * CVPusbIO::ErrorStr(int16_t iErr)
{
	if (iErr >= 0)
	{
		if (g_errinfo[0].err == E_VPUSB_ERR_MAX)
			VPUSB_InitErrInfo();

		return g_errinfo[CLAMP(iErr, 0, E_VPUSB_ERR_MAX)].szerr;
	}

	else
		return libusb_error_name(iErr);
}

int CVPusbIO::Close()
{
	CODETRACE;
	int r = 0;
	if (!IsOpen())
		return r;

	CancelARXfer();
	ClearContThreadFlag();

	for (int i = 0; i < m_devinfo.usbNumInterfaces; i++)
		r = libusb_release_interface(MYHND, i);


	libusb_close(MYHND);

	StopContThread();

	m_pdevhnd = 0;
	m_devinfo = vp_usbdevinfo();

	CODETRACEEX;

	return r;
}

int CVPusbIO::Write(uint8_t *pbuf, int & count)
{
	DOUTHEXBUF(pbuf, count, true, __FUNCTION__);
	int nActual = 0;
	int r = 0;

	if ((r = libusb_bulk_transfer(MYHND, m_devinfo.ep_out, pbuf, count, &nActual, WriteCmdTO())))// 50))
	{
		//r = (r << 16) | E_VPERR_LIBUSB
	}
	else if (nActual != count)
	{
		count = nActual;
		r = E_VPUSB_ERR_WRITE_COUNT_WRONG;
		DOUT1("CVPusbIO write count %d, libusb wrote %d\r\n", count, nActual);
	}
	else if ((nActual % m_devinfo.epout_maxPktsize) == 0)
	{
		r = libusb_bulk_transfer(MYHND, m_devinfo.ep_out, nullptr, 0, &nActual, WriteCmdTO());
	}

	CODETRACEEX;
	return r;

}
// pbuf	 : in  :pointer to buffer to place input data
// count : in  : size of pbuf buffer. 
//       : out : number of bytes read into buffer
int CVPusbIO::Read( uint8_t *pbuf, int & count, bool bTOisErr/*=false*/)
{
	CODETRACE;

	int r = 0;
	int nActual = 0;

	r = libusb_bulk_transfer(MYHND, m_devinfo.ep_in, pbuf, count, &nActual, ReadCmdTO());// 50);
	// timeout is not an error
	//if ((r == LIBUSB_ERROR_TIMEOUT) && !ReadCmdTOIsError())
	if ((r == LIBUSB_ERROR_TIMEOUT) && !bTOisErr)
	{
		r = 0;
		count = 0;
	}
	else
		count = nActual;

	DOUTHEXBUF(pbuf, count, true, __FUNCTION__);
	return r;
}

//uint8_t g_cap_buf[2048];
//uint8_t g_data_in[1024];
libusb_transfer_status g_elastststatus = LIBUSB_TRANSFER_COMPLETED;
//std::mutex g_mtxRxData;

//void WINAPI cb(libusb_transfer *transfer)
//{
//	CVPusbIO * its = (CVPusbIO *)(transfer->user_data);
//	int nBytes = transfer->actual_length;
//	uint8_t * pData = 0;
//	bool bFatal = false;
//
//	switch (transfer->status)
//	{
//			/** Transfer completed without error. Note that this does not indicate
//			* that the entire amount of requested data was transferred. */
//	case LIBUSB_TRANSFER_COMPLETED:
//		pData = transfer->buffer;
//		break;
//			/** Transfer failed */
//	case LIBUSB_TRANSFER_ERROR:
//		break;
//			/** Transfer timed out */
//	case LIBUSB_TRANSFER_TIMED_OUT:
//		break;
//			/** Transfer was cancelled */
//	case LIBUSB_TRANSFER_CANCELLED:
//		bFatal = true;
//		break;
//			/** For bulk/interrupt endpoints: halt condition detected (endpoint
//			* stalled). For control endpoints: control request not supported. */
//	case LIBUSB_TRANSFER_STALL:
//		break;
//			/** Device was disconnected */
//	case LIBUSB_TRANSFER_NO_DEVICE:
//		bFatal = true;
//		break;
//			/** Device sent more data than requested */
//	case LIBUSB_TRANSFER_OVERFLOW:
//		if (nBytes > VPUSB_RAW_READ_BUF_SIZE)
//			nBytes = VPUSB_RAW_READ_BUF_SIZE;
//		pData = transfer->buffer;
//		break;
//	}
//	g_elastststatus = transfer->status;
//
//	/*if (bFatal)
//		its->TransferFatal((int)transfer->status);
//
//	else if (nBytes && pData)
//	{
//		if (its->DataReady(pData, nBytes))
//			libusb_submit_transfer(transfer);
//
//	}*/
//	//{
//	//	std::lock_guard<std::mutex> lck(g_mtxRxData);
//
//	//	if (its->m_iNextHostIndex + transfer->actual_length >= 2048)
//	//		its->m_iNextHostIndex = 0;
//
//	//	memcpy(&(g_cap_buf[its->m_iNextHostIndex]), pData, nBytes);
//	//	its->m_iHostFC++;
//	//	its->m_pLastBufIn = &(g_cap_buf[its->m_iNextHostIndex]);
//	//	its->m_iLastBytesIn = nBytes;
//	//	its->m_iNextHostIndex += nBytes;
//	//}
//	//if (!bFatal && its->m_bContReadRunning)
//	//	libusb_submit_transfer(transfer);
//	//else
//	//	its->m_bContReadRunning = false;
//}

void CVPusbIO::MCB(void *parg)
{
	CODETRACE_ALT5;

	libusb_transfer *transfer = (libusb_transfer*)parg;

	CVPusbIO * its = (CVPusbIO *)(transfer->user_data);
	int nBytes = transfer->actual_length;
	uint8_t * pData = 0;
	bool bFatal = false;
	bool bContRunning = its->AsyncReadRunning();

	bool bKnown = true;
	if (transfer != its->AsyncReadXfer())
		bKnown = false;
	//DOUT5_ALT("%s : %d : %s transfer 0x%x transfer_status = %s, actual_length = %d\r\n", 
	//	__FUNCTION__, __LINE__, (bKnown ? "" : "UNKNOWN") , transfer, libusb_error_name(transfer->status), nBytes);

	if (bKnown)
	{
		switch (transfer->status)
		{
			/** Transfer completed without error. Note that this does not indicate
			* that the entire amount of requested data was transferred. */
		case LIBUSB_TRANSFER_COMPLETED:

			pData = transfer->buffer;
			break;
			/** Transfer failed */
		case LIBUSB_TRANSFER_ERROR:
			DOUT0_ALT("%s :  %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			break;
			/** Transfer timed out */
		case LIBUSB_TRANSFER_TIMED_OUT:
			//DOUT0_ALT("%s :  %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			if (nBytes)
			{
				DOUT3_ALT("%s : %d : %s transfer 0x%x transfer_status = %s nBytes = %d\r\n", 
					__FUNCTION__, __LINE__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status), nBytes);
				pData = transfer->buffer;
			}
			break;
			/** Transfer was cancelled */
		case LIBUSB_TRANSFER_CANCELLED:
			bFatal = true;
			break;
			/** For bulk/interrupt endpoints: halt condition detected (endpoint
			* stalled). For control endpoints: control request not supported. */
		case LIBUSB_TRANSFER_STALL:
			if (nBytes)
				DOUT3_ALT("%s : %d : %s transfer 0x%x transfer_status = %s nBytes = %d\r\n",
					__FUNCTION__, __LINE__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status), nBytes);
			//DOUT0_ALT("%s :  %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			break;
			/** Device was disconnected */
		case LIBUSB_TRANSFER_NO_DEVICE:
			bFatal = true;
			break;
			/** Device sent more data than requested */
		case LIBUSB_TRANSFER_OVERFLOW:
			//DOUT0_ALT("%s :  %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			DOUT3_ALT("%s : %d : %s transfer 0x%x transfer_status = %s nBytes = %d\r\n",
				__FUNCTION__, __LINE__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status), nBytes);
			if (nBytes > VPUSB_RAW_READ_BUF_SIZE)
				nBytes = VPUSB_RAW_READ_BUF_SIZE;
			pData = transfer->buffer;
			break;
		}
		g_elastststatus = transfer->status;

		if (bFatal)
		{
			DOUT0_ALT("%s : %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			its->TransferFatal(transfer); 
		}
		else if (bContRunning)
		{
			if (nBytes && pData)
			{
				//if (nBytes == 60)
				//{
				//	//((SEUPNO_HDR*)pData)->preamble
				//	DOUT0_ALT("%s : %d bytes transferred pdata 0x%x pre %04x fc %u ,transfer_status = %s\r\n", __FUNCTION__, 
				//		nBytes, pData,
				//		((SEUPNO_HDR*)pData)->preamble, ((SEUPNO_HDR*)pData)->seupno.frame,
				//		libusb_error_name(transfer->status));
				//}
				its->DataReady(pData, nBytes);
			}
			if (pData)
				pData[0] = 'X';

			libusb_submit_transfer(transfer);
		}
		else
		{
			DOUT0_ALT("%s : Cont not running.  %s transfer 0x%x transfer_status = %s\r\n", __FUNCTION__, (bKnown ? "" : "UNKNOWN"), transfer, libusb_error_name(transfer->status));
			// not running anymore.. dealloc the transfer here? or let thread do it?
			//its->ClearARXfer();
		}
		
			
	}
	CODETRACEEX_ALT5;
}

void CVPusbIO::TransferFatal( void *vtransfer )
{
	CODETRACE_ALT;
	libusb_transfer *transfer = (libusb_transfer*)vtransfer;
	DOUT5_ALT("%s : transfer 0x%x status = %s\r\n", __FUNCTION__, transfer, libusb_error_name(((libusb_transfer*)transfer)->status));

	//m_bContReadRunning = false;

	//if (transfer == AsyncReadXfer())
	//	ClearARXfer();

	CODETRACEEX_ALT;
}


void  CVPusbIO::ClearARXfer()
{
	if (m_parxfer)
	{
		libusb_free_transfer((libusb_transfer*)m_parxfer);
		m_parxfer = 0;
	}
}

void CVPusbIO::CancelARXfer()
{
	CODETRACE;
	if (m_parxfer)
	{
		int r = 0;
		if ((r = libusb_cancel_transfer((libusb_transfer*)m_parxfer)))
		{
			DOUT0("%s : libusb_cancel_tranfer error : %s\r\n", __FUNCTION__, libusb_error_name(r));
		}
	}
	else
		DOUT3("%s : m_parxfer is ZERO\r\n");

	CODETRACEEX;
}
int CVPusbIO::ReadCont()
{
	return StartAsyncRead();
}
int CVPusbIO::StartAsyncRead()
{
	//DOUT0("Entering %s \r\n", __FUNCTION__);
	CODETRACE;
	if (m_bContReadRunning || m_parxfer)
		return -1;

	int r = 0;
	//libusb_transfer * transfer = 0;
	m_parxfer = libusb_alloc_transfer(0);

	m_data_in[0] = 'X';
	//libusb_fill_bulk_transfer(transfer, MYHND, m_devinfo.ep_in, m_data_in, VPUSB_RAW_READ_BUF_SIZE, cb, (void*)this, ReadTO());
	libusb_fill_bulk_transfer((libusb_transfer*)m_parxfer, MYHND, m_devinfo.ep_in, m_data_in, /*64*/VPUSB_RAW_READ_BUF_SIZE, (libusb_transfer_cb_fn)MCB, (void*)this, 0);// ReadCmdTO());// 50);// AsyncReadTOMS());
	if ((r = libusb_submit_transfer((libusb_transfer*)m_parxfer)))
	{
		DOUT0("%s : libusb_submit_transfer error : %s\r\n", __FUNCTION__, libusb_strerror((enum libusb_error)r));
		m_bContReadRunning = false;
		ClearARXfer();
	}
	else
	{
		DOUT0("%s : libusb_submit_transfer success : MY_AR_XFER = 0x%x\r\n", __FUNCTION__, MY_AR_XFER);
		m_iNextHostIndex = 0;
		m_iHostFC = 0;
		m_iLastBytesIn = 0;
		m_pLastBufIn = 0;
		g_elastststatus = LIBUSB_TRANSFER_COMPLETED;

		//start thread
		StartContThread();
	}
	CODETRACEEX;
	//DOUT0("Leaving %s \r\n", __FUNCTION__);
	return r;
}

void CVPusbIO::DevReport(std::string & s) const
{
	std::stringstream ss;
	ss.str(s);
	ss << std::hex << m_devinfo.usbVID << ":" << m_devinfo.usbPID;
	s = ss.str();
}

void CVPusbIO::StartContThread()
{
	//DOUT0("Entering %s \r\n", __FUNCTION__);
	CODETRACE;

	if (m_thidCont == std::thread::id())
	{
		std::thread *pth = new std::thread(&CVPusbIO::thproc_Cont, this);
		m_pthCont = pth;
		m_thidCont = pth->get_id();

		DOUT0("%s : Thread started id = 0x%x\r\n", __FUNCTION__, m_thidCont);
		//trTracef(0, "STARTED  0x%x\r\n", thidHDisc_);
	}
	else // thread is already running
	{
		DOUT0("%s : Thread id = 0x%x already RUNNING\r\n", __FUNCTION__, m_thidCont);
		//trTracef(0, "StartHDiscThread error : thread already running id = %d\r\n", thidHDisc_);
	}
	CODETRACEEX;
}

bool CVPusbIO::ThreadRunning(std::thread::id thid, std::thread *pth)
{
	return ((thid != std::thread::id()) && (pth != 0));
}
void CVPusbIO::thproc_Cont()
{
	//DOUT0("Entering %s \r\n", __FUNCTION__);
	CODETRACE_ALT5;
	HANDLE hThread = ::GetCurrentThread();
	::SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);// THREAD_PRIORITY_ABOVE_NORMAL);

	m_bContReadRunning = true;

	while (m_bContReadRunning)
		libusb_handle_events(MYCTX);

	//timeval tv;
	//tv.tv_sec = 0;
	//tv.tv_usec = 0;
	//int completed = 0;
	//while (m_bContReadRunning)
	//	libusb_handle_events_timeout_completed(MYCTX, &tv, NULL);// &completed);

	ClearARXfer();
	//DOUT0("Leaving %s \r\n", __FUNCTION__);
	CODETRACEEX_ALT5;
}

void CVPusbIO::StopContThread()
{
	//DOUT0("Entering %s \r\n", __FUNCTION__);
	CODETRACE;
	//if ((!m_bContReadRunning) || (m_pthCont == 0))
	if (!(ThreadRunning(m_thidCont, m_pthCont)))
	{
		std::ostringstream abc;
		abc << m_thidCont;
		std::string str = abc.str();

		//DOUT0("%s : m_bContReadRunning = %s, m_pthCont = 0x%s\r\n", __FUNCTION__, (m_bContReadRunning ? "true" : "false"), m_pthCont);
		DOUT0("%s : Thread not running : m_thidCont = %s, m_pthCont = 0x%s\r\n", __FUNCTION__, str.c_str() , m_pthCont);
		DOUT0("Leaving %s \r\n", __FUNCTION__);
		return;
	}

	//trTracef(0, "StopHDiscThread....");

	m_bContReadRunning = false;

	std::this_thread::sleep_for(std::chrono::milliseconds(100));// ::Sleep(100);
	if (m_pthCont->joinable())
	{
		// join thread and wait for it to terminate
		DOUT0("%s : Thread joinable, joining\r\n", __FUNCTION__);
		m_pthCont->join();
		DOUT0("%s : Thread JOINED\r\n", __FUNCTION__);
	}
	else
	{
		DOUT0("%s : Thread NOT joinable\r\n", __FUNCTION__);

	}
	delete m_pthCont;
	m_pthCont = 0;
	m_thidCont = std::thread::id();
	//trTracef(0, "STOPPED\r\n");
	DOUT0("%s : Thread STOPPED\r\n", __FUNCTION__);

	CODETRACEEX;
	//DOUT0("Leaving %s \r\n", __FUNCTION__);
}

//int CVPusbIO::ReadContStop()
//{
//	StopContThread();
//	return 0;
//}


int CVPusbIO::LastAsyncRead(uint8_t * & pbuf, int & nbytes)
{
	std::lock_guard<std::mutex> lck(m_mtxRxData);

	pbuf = m_pLastBufIn;
	nbytes = m_iLastBytesIn;
	return 0;
}
