//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "CollectionMain.h"
#include "UsagesInfo.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "JvHidControllerClass"
#pragma resource "*.dfm"
TCollectionDemoForm *CollectionDemoForm;
//---------------------------------------------------------------------------
__fastcall TCollectionDemoForm::TCollectionDemoForm(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TCollectionDemoForm::FormShow(TObject *Sender)
{
  // this compensates the possibility that HidCtlDeviceChange
  // may not be able to fill the treeview the first time
  HidCtlDeviceChange(this);
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::HidCtlDeviceChange(TObject *Sender)
{
  // HidCtlDeviceChange happens before TCollectionDemoForm.Create completed
  // DeviceTree may have not been created yet
  if(DeviceTree != NULL)
  {
    // each time something happens to the HID devices the treeview is filled
    DeviceTree->Items->Clear();
    Root = DeviceTree->Items->Add(NULL, "HID-Devices");
    HidCtl->Enumerate();
  }
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::EnumerateButtonCaps(TJvHidDevice *HidDev,
          TTreeNode *Parent, WORD Idx, THIDPReportType ReportType,
          char *ReportText)
{
  int I;
  int Ret;
  WORD N;
  AnsiString UsagePageText;
  AnsiString UsageText;
  AnsiString S;
  // 256 Caps entries should be always sufficient
  THIDPButtonCaps BtnCaps[256];

  N = 255;
  // get ALL ButtonCaps of the specified ReportType
  HidDev->ReportTypeParam = ReportType;
  Ret = HidDev->GetSpecificButtonCaps(BtnCaps, N);
  if(Ret == HIDP_STATUS_SUCCESS)
    for(I = 0; I < N; I++)
      if(BtnCaps[I].LinkCollection == Idx)
      {
        UsageAndUsagePageText(BtnCaps[I].UsagePage, BtnCaps[I].Usage, &UsagePageText, &UsageText);
        if(BtnCaps[I].IsRange)
          DeviceTree->Items->AddChild(Parent,
            S.sprintf("Button %s Range: UsagePage=%s (0x%04X) %d..%d",
              ReportText, UsagePageText, BtnCaps[I].UsagePage, BtnCaps[I].UsageMin, BtnCaps[I].UsageMax));
        else
          DeviceTree->Items->AddChild(Parent,
            S.sprintf("Button %s: UsagePage=%s (0x%04X) Usage=%s (0x%04X)",
              ReportText, UsagePageText, BtnCaps[I].UsagePage, UsageText, BtnCaps[I].Usage));
      }
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::EnumerateValueCaps(TJvHidDevice *HidDev,
          TTreeNode *Parent, WORD Idx, THIDPReportType ReportType,
          char *ReportText)
{
  int I;
  int Ret;
  WORD N;
  AnsiString UsagePageText;
  AnsiString UsageText;
  AnsiString S;
  // 256 Caps entries should be always sufficient
  THIDPValueCaps ValCaps[256];

  N = 255;
  // get ALL ButtonCaps of the specified ReportType
  HidDev->ReportTypeParam = ReportType;
  Ret = HidDev->GetSpecificValueCaps(ValCaps, N);
  if(Ret == HIDP_STATUS_SUCCESS)
    for(I = 0; I < N; I++)
      if(ValCaps[I].LinkCollection == Idx)
      {
        UsageAndUsagePageText(ValCaps[I].UsagePage, ValCaps[I].Usage, &UsagePageText, &UsageText);
        if(ValCaps[I].IsRange)
          DeviceTree->Items->AddChild(Parent,
            S.sprintf("Button %s Range: UsagePage=%s (0x%04X) %d..%d",
              ReportText, UsagePageText, ValCaps[I].UsagePage, ValCaps[I].UsageMin, ValCaps[I].UsageMax));
        else
          DeviceTree->Items->AddChild(Parent,
            S.sprintf("Button %s: UsagePage=%s (0x%04X) Usage=%s (0x%04X)",
              ReportText, UsagePageText, ValCaps[I].UsagePage, UsageText, ValCaps[I].Usage));
      }
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::EnumerateCaps(TJvHidDevice *HidDev,
  TTreeNode *Parent, Word Idx)
{
  // set the params to get ALL Caps
  HidDev->UsagePageParam      = 0;
  HidDev->UsageParam          = 0;
  HidDev->LinkCollectionParam = HIDP_LINK_COLLECTION_UNSPECIFIED;
  EnumerateButtonCaps(HidDev, Parent, Idx, HidP_Input,   "Input");
  EnumerateButtonCaps(HidDev, Parent, Idx, HidP_Output,  "Output");
  EnumerateButtonCaps(HidDev, Parent, Idx, HidP_Feature, "Feature");
  EnumerateValueCaps (HidDev, Parent, Idx, HidP_Input,   "Input");
  EnumerateValueCaps (HidDev, Parent, Idx, HidP_Output,  "Output");
  EnumerateValueCaps (HidDev, Parent, Idx, HidP_Feature, "Feature");
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::EnumerateNodes(TJvHidDevice *HidDev,
  TTreeNode *Parent, Word Idx, Word NumSiblings)
{
  Word I;
  TTreeNode *Node;
  AnsiString UsagePageText;
  AnsiString UsageText;
  AnsiString CollectionTypeText;
  AnsiString NodeText;

  // add a list of sibling nodes to the device tree node Parent
  for(I = 1; I <= NumSiblings; I++)
  {
    UsageAndUsagePageText(HidDev->LinkCollectionNodes[Idx].LinkUsagePage,
      HidDev->LinkCollectionNodes[Idx].LinkUsage, &UsagePageText, &UsageText);
    if(HidDev->LinkCollectionNodes[Idx].CollectionType >= 0x07 &&
       HidDev->LinkCollectionNodes[Idx].CollectionType <= 0x7F)
        CollectionTypeText.sprintf("Reserved 0x%02X",
          Cardinal(HidDev->LinkCollectionNodes[Idx].CollectionType));
    else
      switch(HidDev->LinkCollectionNodes[Idx].CollectionType)
      {
        case 0x00:
          CollectionTypeText = "Physical";
          break;
        case 0x01:
          CollectionTypeText = "Application";
          break;
        case 0x02:
          CollectionTypeText = "Logical";
          break;
        case 0x03:
          CollectionTypeText = "Report";
          break;
        case 0x04:
          CollectionTypeText = "Named Array";
          break;
        case 0x05:
          CollectionTypeText = "Usage Switch";
          break;
        case 0x06:
          CollectionTypeText = "Usage Modifier";
          break;
        default:
          CollectionTypeText.sprintf("Vendor-defined $%.2x",
            Cardinal(HidDev->LinkCollectionNodes[Idx].CollectionType));
          break;
      }

    NodeText.sprintf("UsagePage=%s  (0x%04X)  Usage=%s  (0x%04X)  CollectionType=%s (0x%02X)",
      UsagePageText, HidDev->LinkCollectionNodes[Idx].LinkUsagePage,
      UsageText, HidDev->LinkCollectionNodes[Idx].LinkUsage,
      CollectionTypeText, HidDev->LinkCollectionNodes[Idx].CollectionType);
    if(HidDev->LinkCollectionNodes[Idx].IsAlias != 0)
      NodeText = NodeText + "  IsAlias";
    Node = DeviceTree->Items->AddChild(Parent, NodeText);

    EnumerateCaps(HidDev, Node, Idx);

    // recurse to the children nodes
    if(HidDev->LinkCollectionNodes[Idx].FirstChild != 0)
      EnumerateNodes(HidDev, Node,
        HidDev->LinkCollectionNodes[Idx].FirstChild,
        HidDev->LinkCollectionNodes[Idx].NumberOfChildren);
    // secure against buggy descriptors
    if(HidDev->LinkCollectionNodes[Idx].NextSibling != 0)
      // follow the link to the next sibling
      Idx = HidDev->LinkCollectionNodes[Idx].NextSibling;
    else
      break;
  }
}
//---------------------------------------------------------------------------

bool __fastcall TCollectionDemoForm::HidCtlEnumerate(
      TJvHidDevice *HidDev, const int Idx)
{
  Word Index;
  TTreeNode *Node;
  AnsiString Name;

  // give the treeview node for the device a descriptive name
  if(HidDev->ProductName != NULL)
    Name.sprintf("\"%S\"  VID=0x%.4X PID=0x%.4X",
      HidDev->ProductName, HidDev->Attributes.VendorID, HidDev->Attributes.ProductID);
  else
    Name.sprintf("VID=0x%.4X PID=0x%.4X",
      HidDev->Attributes.VendorID, HidDev->Attributes.ProductID);
  Node = DeviceTree->Items->AddChild(Root, Name);

  // there is only one root node, but we try to enumerate siblings anyway
  // in case there is a bad descriptor
  Index = 0;
  do
  {
    EnumerateNodes(HidDev, Node, Idx, 1);
    Index = HidDev->LinkCollectionNodes[Index].NextSibling;
  }
  while(Index != 0);

  DeviceTree->FullExpand();
  return(true);
}
//---------------------------------------------------------------------------

void __fastcall TCollectionDemoForm::SaveClick(TObject *Sender)
{
  // write the treeview content to a file to avoid the need
  // to send screenshots
  if(SaveDialog->Execute())
    DeviceTree->SaveToFile(SaveDialog->FileName);
}
//---------------------------------------------------------------------------

