// BulkLoopDlg.cpp : implementation file
//

#include "stdafx.h"
#include "BulkLoop.h"
#include "BulkLoopDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif



/////////////////////////////////////////////////////////////////////////////
// Fills buf with data per the selected method
void stuffBuff(PUCHAR buf, LONG len, LONG seed, int method) {

	DWORD *dwBuf = (DWORD *) buf;
	srand((UINT)seed);

	int cnt = (method == 3) ? len / 4 : len;
	for (int i=0; i<cnt; i++) 
		switch (method) {
		case 0:
			buf[i] = (CHAR) seed;
			break;
		case 1:
			buf[i] = rand();
			break;
		case 2:
			buf[i] = seed + i;
			break;
		case 3:
			dwBuf[i] = seed + i;
			break;
		}

}


/////////////////////////////////////////////////////////////////////////////
// The looping Bulk Xfer Thread function
//
// The looping xfers need to run in a separate thread so as to be stoppable 
// via a click on the "Stop" button.  Otherwise, that click will never get 
// serviced and the app will loop indefinitely.
//
// The "Start" button on click handler just calls this routine, passing a pointer
// to the dialog (main window) object.  That way, this routine has access to all
// the public members of the CBulkLoopDlg class.

UINT XferLoop( LPVOID params ) {

    OVERLAPPED outOvLap, inOvLap; 

    CBulkLoopDlg *dlg = (CBulkLoopDlg *) params;
	char s[24];						ZeroMemory(s, 24);
	
	dlg->m_XferSize.GetLine(0,s,8);
	LONG xfer = atol(s);
	PUCHAR data = new UCHAR[xfer];	ZeroMemory(data,xfer);
	PUCHAR inData = new UCHAR[xfer];	ZeroMemory(inData,xfer);

    outOvLap.hEvent  = CreateEvent(NULL, false, false, "CYUSB_OUT"); 
    inOvLap.hEvent   = CreateEvent(NULL, false, false, "CYUSB_IN"); 

	dlg->m_SeedValue.GetLine(0,s,8);
	LONG seed = atol(s);

	int stopOnError = dlg->m_StopOnErrorChkBox.GetCheck();

	stuffBuff(data,xfer,seed,dlg->m_FillPatternComBox.GetCurSel());

	dlg->m_StatusLabel.SetWindowText(" STATUS: Transferring data . . .");
	dlg->m_SuccessCount.SetWindowText("0");
	dlg->m_FailureCount.SetWindowText("0");

	bool success;
	LONG nSuccess = 0;
	LONG nFailure = 0;

    if (dlg->m_DisableTimeoutChkBox.GetCheck())
    {
        dlg->OutEndpt->TimeOut = 0;
	    dlg->InEndpt->TimeOut = 0;
    }
    else
    {
        dlg->OutEndpt->TimeOut = 2000;
	    dlg->InEndpt->TimeOut = 2000;
    }

	for (;dlg->bLooping;) {
        LONG outlen,inlen,len;

        outlen = inlen = len = xfer;     // Use temp var because XferData can change the value of len

	    UCHAR  *outContext = dlg->OutEndpt->BeginDataXfer(data,outlen,&outOvLap);
	    UCHAR  *inContext = dlg->InEndpt->BeginDataXfer(inData,inlen,&inOvLap);

        dlg->OutEndpt->WaitForXfer(&outOvLap,2000); 
        dlg->InEndpt->WaitForXfer(&inOvLap,2000); 

        success = dlg->OutEndpt->FinishDataXfer(data, outlen, &outOvLap,outContext); 
        success = dlg->InEndpt->FinishDataXfer(inData,inlen, &inOvLap,inContext); 


		if (success) {
			bool pass = (memcmp(data,inData,len) == 0);
			if (pass)
				nSuccess++;
			else
				nFailure++;
		} else 
			nFailure++;

		sprintf(s,"%d",nSuccess);
		dlg->m_SuccessCount.SetWindowText(s);
		sprintf(s,"%d",nFailure);
		dlg->m_FailureCount.SetWindowText(s);

		if ((!success) && stopOnError) dlg->bLooping = false;
	} 

    CloseHandle(outOvLap.hEvent); 
    CloseHandle(inOvLap.hEvent); 

	delete [] data;
	delete [] inData;

	dlg->m_StatusLabel.SetWindowText(" STATUS: Stopped");

	dlg->XferThread = NULL;

    dlg->USBDevice->Close();

	return true;
}


/////////////////////////////////////////////////////////////////////////////
// CBulkLoopDlg dialog

CBulkLoopDlg::CBulkLoopDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CBulkLoopDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CBulkLoopDlg)
	m_DataValueRadioBtns = 0;
	//}}AFX_DATA_INIT

	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDI_CYICON);

}

void CBulkLoopDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CBulkLoopDlg)
	DDX_Control(pDX, IDC_DISABLE_TIMEOUT, m_DisableTimeoutChkBox);
	DDX_Control(pDX, IDC_TEST_BTN, m_TestBtn);
	DDX_Control(pDX, IDC_RESET_BTN, m_RefreshBtn);
	DDX_Control(pDX, IDC_DEVICELIST_COMBOX, m_DeviceListComBox);
	DDX_Control(pDX, IDC_START_BTN, m_StartBtn);
	DDX_Control(pDX, IDC_STATUS_LABEL, m_StatusLabel);
	DDX_Control(pDX, IDC_FILLPATTERN_COMBOX, m_FillPatternComBox);
	DDX_Control(pDX, IDC_STOP_ON_ERROR_CHKBOX, m_StopOnErrorChkBox);
	DDX_Control(pDX, IDC_SEED_EDIT, m_SeedValue);
	DDX_Control(pDX, IDC_FAILURE_LABEL, m_FailureCount);
	DDX_Control(pDX, IDC_SUCCESS_LABEL, m_SuccessCount);
	DDX_Control(pDX, IDC_XFERSIZE_EDIT, m_XferSize);
	DDX_Control(pDX, IDC_IN_COMBOX, m_InEndptComBox);
	DDX_Control(pDX, IDC_OUT_COMBOX, m_OutEndptComBox);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CBulkLoopDlg, CDialog)
	//{{AFX_MSG_MAP(CBulkLoopDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_START_BTN, OnStartBtn)
	ON_CBN_SELCHANGE(IDC_OUT_COMBOX, OnSelchangeOutCombox)
	ON_CBN_SELCHANGE(IDC_IN_COMBOX, OnSelchangeInCombox)
	ON_BN_CLICKED(IDC_RESET_BTN, OnResetBtn)
	ON_BN_CLICKED(IDC_REFRESH_BTN, OnRefreshBtn)
	ON_BN_CLICKED(IDC_TEST_BTN, OnTestBtn)
	ON_CBN_SELCHANGE(IDC_DEVICELIST_COMBOX, OnSelchangeDeviceListCombox)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CBulkLoopDlg message handlers

BOOL CBulkLoopDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	// when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	

	XferThread = NULL;

	USBDevice = new CCyUSBDevice(m_hWnd);   // Create an instance of CCyUSBDevice

    OnRefreshBtn();

	m_XferSize.SetWindowText("1024");
	m_SeedValue.SetWindowText("2");
	m_FillPatternComBox.SetCurSel(0);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CBulkLoopDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	CDialog::OnSysCommand(nID, lParam);	
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CBulkLoopDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CBulkLoopDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

//___________________________________________________________________________

void CBulkLoopDlg::OnStartBtn() 
{
	if (XferThread) {
		bLooping = false;

//		InEndpt->Abort();
//		InEndpt->Reset();

//		OutEndpt->Abort();
//		OutEndpt->Reset();

		m_StartBtn.SetWindowText("Start");
	} else {

		char s[12];
		ZeroMemory(s,12);
		m_XferSize.GetLine(0,s,8);
		LONG xfer = atol(s);
		if (xfer > 2048) {
			xfer = 2048;
			m_XferSize.SetWindowText("2048");
		}

        USBDevice->Open(DeviceIndex);
        OutEndpt = USBDevice->EndPoints[m_OutEndptComBox.GetItemData(m_OutEndptComBox.GetCurSel())];
        InEndpt = USBDevice->EndPoints[m_InEndptComBox.GetItemData(m_InEndptComBox.GetCurSel())];

		// Launch the looping thread  (Calls XferLoop() function, above.)
		if (xfer && USBDevice->IsOpen()) {
			bLooping = true;
			XferThread = AfxBeginThread(XferLoop, this);
		}

		m_StartBtn.SetWindowText("Stop");
	}
	
}

//___________________________________________________________________________

void CBulkLoopDlg::OnSelchangeOutCombox() 
{
//	OutEndpt = (CCyUSBEndPoint *) m_OutEndptComBox.GetItemDataPtr(m_OutEndptComBox.GetCurSel());
}

//___________________________________________________________________________

void CBulkLoopDlg::OnSelchangeInCombox() 
{
//	InEndpt = (CCyUSBEndPoint *) m_InEndptComBox.GetItemDataPtr(m_InEndptComBox.GetCurSel());
}

//___________________________________________________________________________

void CBulkLoopDlg::OnSelchangeDeviceListCombox() 
{
    int i;
    char s[12];

    DeviceIndex = m_DeviceListComBox.GetCurSel();

    USBDevice->Open(DeviceIndex);

    int epts = USBDevice->EndPointCount();
    CCyUSBEndPoint *endpt;

	// Load the endpoint combo boxes
    // Skip endpoint 0, which we know is the Control Endpoint
	for (i=1; i<epts; i++)
    {    
        endpt = USBDevice->EndPoints[i];

        if (endpt->Attributes == 2)    // Bulk
        {
            sprintf(s, "0x%02X", endpt->Address);

            if (endpt->Address & 0x80)
            {
                m_InEndptComBox.AddString(s);
                m_InEndptComBox.SetItemData(m_InEndptComBox.GetCount()-1,i);
            }
            else
            {
                m_OutEndptComBox.AddString(s);
                m_OutEndptComBox.SetItemData(m_OutEndptComBox.GetCount()-1,i);
            }
        }
    }

    m_InEndptComBox.SetCurSel(0);
	m_OutEndptComBox.SetCurSel(0);

    OnSelchangeInCombox();		// Set the InEndpt member
	OnSelchangeOutCombox();		// Set the OutEndpt member

}


void CBulkLoopDlg::OnResetBtn() 
{
    if (XferThread) OnStartBtn();  // Hit the "Stop" button, resetting endpoints
	
	USBDevice->Reset();	

	delete USBDevice;
	
	// Clear the combo boxes
	m_InEndptComBox.ResetContent();  
	m_OutEndptComBox.ResetContent();

	// Temporarily save the Xfer size and Seed values
	char s1[12];
	char s2[12];
	m_XferSize.GetWindowText(s1,8);
	m_SeedValue.GetWindowText(s2,8);
	int fillmethod = m_FillPatternComBox.GetCurSel();

	// Re-initialize the combo boxes, USBDevice, etc
	OnInitDialog();

	// Restore the previous values for these controls
	m_XferSize.SetWindowText(s1);
	m_SeedValue.SetWindowText(s2);
	m_FillPatternComBox.SetCurSel(fillmethod);

}


void CBulkLoopDlg::OnRefreshBtn() 
{
    int i;

    // if we are currently running loopback, stop
    if (XferThread)
    {
        bLooping = false;

        InEndpt->Abort();
        InEndpt->Reset();

        OutEndpt->Abort();
        OutEndpt->Reset();

        m_StartBtn.SetWindowText("Start");
    }

    // clear the combo boxes
    m_DeviceListComBox.ResetContent();
	m_InEndptComBox.ResetContent();  
	m_OutEndptComBox.ResetContent();

    // make sure there is at lesat one device out there
    if (USBDevice->DeviceCount())
    {
        // search for all connected devices, and add them to the device
        // combo box
        for (i = 0; i < USBDevice->DeviceCount(); i++)
        {
            USBDevice->Open(i);

            m_DeviceListComBox.AddString(USBDevice->DeviceName);
        }


        m_DeviceListComBox.SetCurSel(0);
        OnSelchangeDeviceListCombox();		// This will set the device index and load the ep lists

    }	
}


void CBulkLoopDlg::OnTestBtn() 
{
  

	
}


