//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
void __stdcall OvDummy(DWORD dwErrorCode, DWORD dwCount, POverlapped lpOvl)
{
}
//---------------------------------------------------------------------------
__fastcall TDevInThread::TDevInThread(TJvHidDevice *MyDev) : TThread(False)
{

  // Initialize variables.
  Dev = MyDev;
}
//---------------------------------------------------------------------------
void __fastcall TDevInThread::HandleReport(void)
{

  // Show input report.
  MainForm->ShowInputReport();
}
//---------------------------------------------------------------------------
void __fastcall TDevInThread::Execute(void)
{
  DWORD dwRetVal;


  dwRetVal = WAIT_IO_COMPLETION;
  while (!Terminated) {
    // Read input report.
    if (!Dev->ReadFileEx(&Report, Dev->Caps.InputReportByteLength, &OvDummy))
      break;
    // Wait read to complete.
    do {
      dwRetVal = SleepEx(100, true);
    } while (!Terminated && (dwRetVal != WAIT_IO_COMPLETION));
    // Handle input report.
    if (!Terminated) {
      dwReportLen = Dev->HidOverlappedReadResult;
      Synchronize(HandleReport);
    }
  }

  // Cancel pending read.
  if (dwRetVal != WAIT_IO_COMPLETION)
    Dev->CancelIO(omhRead);
}
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner):TForm(Owner)
{

  // Initialize variables.
  memset(&OutReport, 0, sizeof(OutReport));
  dwOutReportLen = 0;
  dwOutReportNum = 0;
  bRunning = 0;
  wInRepSize = 0;
  dwInRepCount = 0;
  dwInRepTotal = 0;
  dwInRepTime = 0;
  wOutRepSize = 0;
  dwOutRepCount = 0;
  dwOutRepTotal = 0;
  dwOutRepTime = 0;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::HidCtlDeviceChange(TObject *Sender)
{

  // Simulate stop button click.
  StopButtonClick(this);

  if (DevList == NULL) {
    // Create device list.
    DevList = new TList();
  } else {
    // Free all device list entries.
    for (int i = 0; i < DevList->Count; i++)
      ((TJvHidDevice *)DevList->Items[i])->Free();
    // Clear device list.
    DevList->Clear();
  }

  // Clear device list box.
  DevListBox->Clear();

  // Enumerate all USB HID devices.
  HidCtl->Enumerate();
}
//---------------------------------------------------------------------------
bool __fastcall TMainForm::HidCtlEnumerate(TJvHidDevice *HidDev, const int Idx)
{

  // Add device to device list box.
  AnsiString str;
  DevListBox->Items->Add(str.sprintf("VID=%04X PID=%04X", HidDev->Attributes.VendorID, HidDev->Attributes.ProductID));

  // Checkout device.
  TJvHidDevice *Dev;
  HidCtl->CheckOutByIndex(Dev, Idx);

  // Add device to device list.
  DevList->Add(Dev);

  return true;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::DevListBoxClick(TObject *Sender)
{

  if (DevListBox->ItemIndex != -1) {
    // Get device from device list.
    TJvHidDevice *Dev = (TJvHidDevice *)DevList->Items[DevListBox->ItemIndex];
    // Get device info.
    wInRepSize = Dev->Caps.InputReportByteLength;
    if (wInRepSize)
      wInRepSize--;
    wOutRepSize = Dev->Caps.OutputReportByteLength;
    if (wOutRepSize)
      wOutRepSize--;
    // Show report info.
    InRepSizeEdit->Text = IntToStr(wInRepSize);
    InRepCountEdit->Text = "";
    InRepSpeedEdit->Text = "";
    OutRepSizeEdit->Text = IntToStr(wOutRepSize);
    OutRepCountEdit->Text = "";
    OutRepSpeedEdit->Text = "";
  }
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::StartButtonClick(TObject *Sender)
{

  if (DevListBox->ItemIndex == -1) {
    Application->MessageBox("You must select an USB HID device", "Error",
                            MB_ICONERROR | MB_OK);
  } else {
    // Enable measure timer.
    dwInRepCount = 0;
    dwInRepTotal = 0;
    dwInRepTime = time(NULL);
    dwOutRepCount = 0;
    dwOutRepTotal = 0;
    dwOutRepTime = time(NULL);
    MeasureTimer->Enabled = true;
    // Create device input thread.
    DevInThread = new TDevInThread((TJvHidDevice *)DevList->Items[DevListBox->ItemIndex]);
    // Enable device output timer.
    if (wOutRepSize > 0) {
      dwOutReportNum = 0;
      OutRepTimer->Interval = StrToIntDef(OutRepIntervalEdit->Text, 100);
      OutRepIntervalEdit->Text = IntToStr(OutRepTimer->Interval);
      OutRepTimer->Enabled = true;
    }
    // Enable and disable controls.
    DevListBox->Enabled = false;
    OutRepIntervalEdit->Enabled = false;
    StartButton->Enabled = false;
    StopButton->Enabled = true;
    // Set running flag.
    bRunning = 1;
    // Set focus to log.
    LogRichEdit->SelAttributes->Color = clBlack;
    LogRichEdit->Lines->Append("----------------------------------------");
    LogRichEdit->SetFocus();
  }
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::StopButtonClick(TObject *Sender)
{

  if (bRunning) {
    // Clear running flag.
    bRunning = 0;
    // Enable and disable controls.
    DevListBox->Enabled = true;
    OutRepIntervalEdit->Enabled = true;
    StartButton->Enabled = true;
    StopButton->Enabled = false;
    // Disable device output timer.
    OutRepTimer->Enabled = false;
    // Terminate device input thread.
    DevInThread->Terminate();
    DevInThread->WaitFor();
    delete DevInThread;
    // Disable measure timer.
    MeasureTimer->Enabled = false;
  }
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClearButtonClick(TObject *Sender)
{

  // Clear log.
  LogRichEdit->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MeasureTimerTimer(TObject *Sender)
{
  unsigned long dwRepTime, dwSpeed;


  // Update input and output report count.
  dwInRepTotal += dwInRepCount;
  InRepCountEdit->Text = IntToStr(dwInRepTotal);
  dwOutRepTotal += dwOutRepCount;
  OutRepCountEdit->Text = IntToStr(dwOutRepTotal);

  // Update input and output report speed.
  dwRepTime = time(NULL);
  if (dwRepTime > dwInRepTime) {
    dwSpeed = (wInRepSize * dwInRepCount) / (dwRepTime - dwInRepTime);
    InRepSpeedEdit->Text = IntToStr(dwSpeed);
  }
  if (dwRepTime > dwOutRepTime) {
    dwSpeed = (wOutRepSize * dwOutRepCount) / (dwRepTime - dwOutRepTime);
    OutRepSpeedEdit->Text = IntToStr(dwSpeed);
  }

  // Reset measure counters.
  dwInRepTime = dwRepTime;
  dwInRepCount = 0;
  dwOutRepTime = dwRepTime;
  dwOutRepCount = 0;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::OutRepTimerTimer(TObject *Sender)
{

  // Build output report.
  ((unsigned long *)OutReport.bData)[0] = dwOutReportNum;
  dwOutReportNum++;

  // Write output report.
  TJvHidDevice *Dev = (TJvHidDevice *)DevList->Items[DevListBox->ItemIndex];
  Dev->WriteFile(&OutReport, wOutRepSize + 1, (unsigned int)dwOutReportLen);

  // Show output report.
  ShowOutputReport();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormDestroy(TObject *Sender)
{

  // Simulate stop button click.
  StopButtonClick(this);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ShowInputReport(void)
{

  // Show input report.
  AnsiString str;
  str = "< " + IntToHex(DevInThread->Report.bId, 2) + " ";
  for (DWORD i = 0; i < DevInThread->dwReportLen; i++)
    str += IntToHex(DevInThread->Report.bData[i], 2);
  LogRichEdit->SelAttributes->Color = clBlue;
  LogRichEdit->Lines->Append(str);

  // Increase input report count.
  dwInRepCount++;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ShowOutputReport(void)
{

  // Show output report.
  AnsiString str;
  str = "> " + IntToHex(OutReport.bId, 2) + " ";
  for (DWORD i = 0; i < dwOutReportLen; i++)
    str += IntToHex(OutReport.bData[i], 2);
  LogRichEdit->SelAttributes->Color = clRed;
  LogRichEdit->Lines->Append(str);

  // Increase output report count.
  dwOutRepCount++;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::OutRepIntervalEditChange(TObject *Sender)
{
  int iInterval;


  iInterval = StrToIntDef(OutRepIntervalEdit->Text, 0);
  if (iInterval <= 0)
    OutRepIntervalEdit->Text = "";
}
//---------------------------------------------------------------------------

