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

#include <vcl.h>
#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "wclBluetooth"
#pragma resource "*.dfm"

TfmMain *fmMain;
//---------------------------------------------------------------------------
__fastcall TfmMain::TfmMain(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::FormCreate(TObject *Sender)
{
  wclBluetoothManager->Open();
  Cleanup();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::FormDestroy(TObject *Sender)
{
  wclGattClient->Disconnect();
  wclBluetoothManager->Close();

  Cleanup();
}
//---------------------------------------------------------------------------
TwclBluetoothRadio* __fastcall TfmMain::GetRadio()
{
  TwclBluetoothRadio* Radio;
  int Res = wclBluetoothManager->GetLeRadio(Radio);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Get working radio failed: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return NULL;
  }
  return Radio;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btDiscoverClick(TObject *Sender)
{
  TwclBluetoothRadio* Radio = GetRadio();
  if (Radio != NULL)
  {
    int Res = Radio->Discover(10, dkBle);
    if (Res != WCL_E_SUCCESS)
    {
      MessageDlg("Error starting discovering: 0x" + IntToHex(Res, 8),
        mtError, TMsgDlgButtons() << mbOK, 0);
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btClearEventsClick(TObject *Sender)
{
  lvEvents->Items->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::TraceEvent(__int64 Address, String Event,
  String Param, String Value)
{
  TListItem* Item = lvEvents->Items->Add();
  if (Address == 0)
    Item->Caption = "";
  else
    Item->Caption = IntToHex(Address, 12);
  Item->SubItems->Add(Event);
  Item->SubItems->Add(Param);
  Item->SubItems->Add(Value);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btConnectClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);

  else
  {
    TListItem* Item = lvDevices->Selected;
    try
    {
      wclGattClient->Address = StrToInt64("$" + Item->Caption);
      wclGattClient->ConnectOnRead = cbConnectOnRead->Checked;
      wclGattClient->ForceNotifications = cbForceIndications->Checked;
      int Res = wclGattClient->Connect((TwclBluetoothRadio*)Item->Data);
      if (Res != WCL_E_SUCCESS)
        MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
          TMsgDlgButtons() << mbOK, 0);
    }
    catch(const Exception& e)
    {
      MessageDlg(e.Message, mtError, TMsgDlgButtons() << mbOK, 0);
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btDisconnectClick(TObject *Sender)
{
  int Res = wclGattClient->Disconnect();
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetServicesClick(TObject *Sender)
{
  lvServices->Items->Clear();
  FServices.Length = 0;

  int Res = wclGattClient->ReadServices(OpFlag(), FServices);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return;
  }

  if (FServices.Length == 0)
    return;

  for (int i = 0; i < FServices.Length; i++)
  {
    TwclGattService Service = FServices[i];

    TListItem* Item = lvServices->Items->Add();
    if (Service.Uuid.IsShortUuid)
      Item->Caption = IntToHex(Service.Uuid.ShortUuid, 4);
    else
      Item->Caption = Sysutils::GUIDToString(Service.Uuid.LongUuid);
    Item->SubItems->Add(BoolToStr(Service.Uuid.IsShortUuid, true));
    Item->SubItems->Add(IntToHex(Service.Handle, 4));
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetIncludedServicesClick(TObject *Sender)
{
  if (lvServices->Selected == NULL)
  {
    MessageDlg("Select service", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattService Service = FServices[lvServices->Selected->Index];

  lvServices->Items->Clear();
  FServices.Length = 0;

  int Res = wclGattClient->ReadIncludedServices(Service, OpFlag(), FServices);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return;
  }

  if (FServices.Length == 0)
    return;

  for (int i = 0; i < FServices.Length; i++)
  {
    Service = FServices[i];

    TListItem* Item = lvServices->Items->Add();
    if (Service.Uuid.IsShortUuid)
      Item->Caption = IntToHex(Service.Uuid.ShortUuid, 4);
    else
      Item->Caption = Sysutils::GUIDToString(Service.Uuid.LongUuid);
    Item->SubItems->Add(BoolToStr(Service.Uuid.IsShortUuid, true));
    Item->SubItems->Add(IntToHex(Service.Handle, 4));
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetCharacteristicsClick(TObject *Sender)
{
  FCharacteristics.Length = 0;
  lvCharacteristics->Items->Clear();

  if (lvServices->Selected == NULL)
  {
    MessageDlg("Select service", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattService Service = FServices[lvServices->Selected->Index];
  int Res = wclGattClient->ReadCharacteristics(Service, OpFlag(),
    FCharacteristics);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return;
  }

  if (FCharacteristics.Length == 0)
    return;

  for (int i = 0; i < FCharacteristics.Length; i++)
  {
    TwclGattCharacteristic Character = FCharacteristics[i];

    TListItem* Item = lvCharacteristics->Items->Add();
    if (Character.Uuid.IsShortUuid)
      Item->Caption = IntToHex(Character.Uuid.ShortUuid, 4);
    else
      Item->Caption = Sysutils::GUIDToString(Character.Uuid.LongUuid);
    Item->SubItems->Add(BoolToStr(Character.Uuid.IsShortUuid, true));
    Item->SubItems->Add(IntToHex(Character.ServiceHandle, 4));
    Item->SubItems->Add(IntToHex(Character.Handle, 4));
    Item->SubItems->Add(IntToHex(Character.ValueHandle, 4));
    Item->SubItems->Add(BoolToStr(Character.IsBroadcastable, true));
    Item->SubItems->Add(BoolToStr(Character.IsReadable, true));
    Item->SubItems->Add(BoolToStr(Character.IsWritable, true));
    Item->SubItems->Add(BoolToStr(Character.IsWritableWithoutResponse, true));
    Item->SubItems->Add(BoolToStr(Character.IsSignedWritable, true));
    Item->SubItems->Add(BoolToStr(Character.IsNotifiable, true));
    Item->SubItems->Add(BoolToStr(Character.IsIndicatable, true));
    Item->SubItems->Add(BoolToStr(Character.HasExtendedProperties, true));
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetCharValueClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];
  TwclGattCharacteristicValue Value;
  int Res = wclGattClient->ReadCharacteristicValue(Characteristic, OpFlag(),
    Value, Protection());
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return;
  }

  if (Value.Length == 0)
  {
    MessageDlg("Value is empty", mtInformation, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  String Str = "";
  for (int i = 0; i < Value.Length; i++)
    Str = Str + IntToHex(Value[i], 2);
  MessageDlg("Value: " + Str, mtInformation, TMsgDlgButtons() << mbOK, 0);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetDescriptorsClick(TObject *Sender)
{
  FDescriptors.Length = 0;
  lvDescriptors->Items->Clear();

  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];
  int Res = wclGattClient->ReadDescriptors(Characteristic, OpFlag(),
    FDescriptors);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
    return;
  }

  if (FDescriptors.Length == 0)
    return;

  for (int i = 0; i < FDescriptors.Length; i++)
  {
    TwclGattDescriptor Descriptor = FDescriptors[i];

    TListItem* Item = lvDescriptors->Items->Add();
    if (Descriptor.Uuid.IsShortUuid)
      Item->Caption = IntToHex(Descriptor.Uuid.ShortUuid, 4);
    else
      Item->Caption = Sysutils::GUIDToString(Descriptor.Uuid.LongUuid);
    Item->SubItems->Add(BoolToStr(Descriptor.Uuid.IsShortUuid, true));
    Item->SubItems->Add(IntToHex(Descriptor.ServiceHandle, 4));
    Item->SubItems->Add(IntToHex(Descriptor.CharacteristicHandle, 4));
    Item->SubItems->Add(IntToHex(Descriptor.Handle, 4));
    switch (Descriptor.DescriptorType)
    {
      case dtCharacteristicExtendedProperties:
        Item->SubItems->Add("dtCharacteristicExtendedProperties");
        break;
      case dtCharacteristicUserDescription:
        Item->SubItems->Add("dtCharacteristicUserDescription");
        break;
      case dtClientCharacteristicConfiguration:
        Item->SubItems->Add("dtClientCharacteristicConfiguration");
        break;
      case dtServerCharacteristicConfiguration:
        Item->SubItems->Add("dtServerCharacteristicConfiguration");
        break;
      case dtCharacteristicFormat:
        Item->SubItems->Add("dtCharacteristicFormat");
        break;
      case dtCharacteristicAggregateFormat:
        Item->SubItems->Add("dtCharacteristicAggregateFormat");
        break;
      default:
        Item->SubItems->Add("dtCustomDescriptor");
        break;
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Cleanup()
{
  FCharacteristics.Length = 0;
  FDescriptors.Length = 0;
  FServices.Length = 0;

  lvServices->Items->Clear();
  lvCharacteristics->Items->Clear();
  lvDescriptors->Items->Clear();
  lvDescriptorValue->Items->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btGetDescValueClick(TObject *Sender)
{
  lvDescriptorValue->Items->Clear();

  if (lvDescriptors->Selected == NULL)
  {
    MessageDlg("Select descriptor", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattDescriptor Descriptor = FDescriptors[lvDescriptors->Selected->Index];
  TwclGattDescriptorValue Value;
  int Res = wclGattClient->ReadDescriptorValue(Descriptor, OpFlag(),
    Value, Protection());
  if (Res != WCL_E_SUCCESS)
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  else
  {
    switch (Value.AType)
    {
      case dtCharacteristicExtendedProperties:
        ListProp("Type", "dtCharacteristicExtendedProperties");
        ListProp("IsReliableWriteEnabled", BoolToStr(
          Value.CharacteristicExtendedProperties.IsReliableWriteEnabled, true));
        ListProp("IsAuxiliariesWritable", BoolToStr(
          Value.CharacteristicExtendedProperties.IsAuxiliariesWritable, true));
        break;

      case dtCharacteristicUserDescription:
        ListProp("Type", "dtCharacteristicUserDescription");
        ListProp("Descriptipon", Value.UserDescription.Description);
        break;

      case dtClientCharacteristicConfiguration:
        ListProp("Type", "dtClientCharacteristicConfiguration");
        ListProp("IsSubscribeToNotification ", BoolToStr(
          Value.ClientCharacteristicConfiguration.IsSubscribeToNotification,
          true));
        ListProp("IsSubscribeToIndication", BoolToStr(
          Value.ClientCharacteristicConfiguration.IsSubscribeToIndication,
          true));
        break;

      case dtServerCharacteristicConfiguration:
        ListProp("Type", "dtServerCharacteristicConfiguration");
        ListProp("IsBroadcast", BoolToStr(
          Value.ServerCharacteristicConfiguration.IsBroadcast, true));
        break;

      case dtCharacteristicFormat:
        ListProp("Type", "dtCharacteristicFormat");
        ListProp("Format", IntToHex((Byte)Value.CharacteristicFormat.Format, 2));
        ListProp("Exponent", IntToHex(
          Value.CharacteristicFormat.Exponent, 2));
        ListProp("Unit", IntToHex(
          Value.CharacteristicFormat.AUnit, 4));
        ListProp("NameSpace", IntToHex(
          Value.CharacteristicFormat.NameSpace, 2));
        ListProp("Description", IntToHex(
          Value.CharacteristicFormat.Description, 4));
        break;

      case dtCharacteristicAggregateFormat:
        ListProp("Type", "dtCharacteristicAggregateFormat");
        if (Value.CharacteristicAggregateFormat.Handles.Length > 0)
        {
          for (int i = 0; i < Value.CharacteristicAggregateFormat.Handles.Length; i++)
          {
            ListProp("Handle [" + IntToStr(i) + "]",
              IntToHex(Value.CharacteristicAggregateFormat.Handles[i], 4));
          }
        }
        break;

      default:
        ListProp("Type", "UNKNOWN");
        break;
    }

    if (Value.Data.Length > 0)
    {
      String Str = "";
      for (int i = 0; i < Value.Data.Length; i++)
        Str = Str + IntToHex(Value.Data[i], 2);
      ListProp("Data", Str);
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::ListProp(String Name, String Value)
{
  TListItem* Item = lvDescriptorValue->Items->Add();
  Item->Caption = Name;
  Item->SubItems->Add(Value);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btSetValueClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];

  String Str = edCharVal->Text;
  if (Str.Length() % 2 != 0)
    Str = "0" + Str;
  TwclGattCharacteristicValue Val;
  Val.Length = Str.Length() / 2;
  int i = 0;
  int j = 0;
  while (i < Str.Length())
  {
    Val[j] = StrToInt("$" + Str.SubString(i, 2));
    j++;
    i += 2;
  }

  int Res;
  TwclGattWriteKind WriteKind;
  switch (cbWriteKind->ItemIndex)
  {
    case 0:
      WriteKind = wkWithResponse;
      break;
    case 1:
      WriteKind = wkWithoutResponse;
      break;
    default:
      WriteKind = wkAuto;
      break;
  }
  
  Res = wclGattClient->WriteCharacteristicValue(Characteristic, Val,
    Protection(), WriteKind);
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btSubscribeClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];

  TwclGattSubscribeKind SubscribeKind;
  switch (cbSubscribeKind->ItemIndex)
  {
    case 0:
      SubscribeKind = skNotification;
      break;
    case 1:
      SubscribeKind = skIndication;
      break;
    default:
      SubscribeKind = skManual;
      break;
  }

  if (SubscribeKind == skManual)
  {
    // In case if characteristic has both Indication and Notification properties
    // set to True we have to select one of them. Here we use Notifications but
    // you can use other one.
    if (Characteristic.IsNotifiable && Characteristic.IsIndicatable)
    {
      // Change the code line below to
      // Characteristic.IsNotifiable = false;
      // if you want to receive Indications instead of notifications.
      Characteristic.IsIndicatable = false;
    }
  }
  
  int Res;
  if (cbFastSubscribe->Checked)
  {
    if (SubscribeKind == skManual)
      Res = wclGattClient->SubscribeForNotifications(Characteristic);
    else
    {
      Res = wclGattClient->SubscribeForNotifications(Characteristic, OpFlag(),
        Protection(), SubscribeKind);
    }
  }
  else
  {
    if (SubscribeKind == skManual)
      Res = wclGattClient->Subscribe(Characteristic);
    else
      Res = wclGattClient->Subscribe(Characteristic, SubscribeKind);
  }

  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btUnsubscribeClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];

  TwclGattSubscribeKind SubscribeKind;
  switch (cbSubscribeKind->ItemIndex)
  {
    case 0:
      SubscribeKind = skNotification;
      break;
    case 1:
      SubscribeKind = skIndication;
      break;
    default:
      SubscribeKind = skManual;
      break;
  }

  if (SubscribeKind == skManual)
  {
    // In case if characteristic has both Indication and Notification properties
    // set to True we have to select one of them. Here we use Notifications but
    // you can use other one.
    // YOU MUST USE THE SAME PROPERTY THAT IS USED IN SUBSCRIBE
    if (Characteristic.IsNotifiable && Characteristic.IsIndicatable)
    {
      // Change the code line below to
      // Characteristic.IsNotifiable = false;
      // if you want to receive Indications instead of notifications.
      Characteristic.IsIndicatable = false;
    }
  }
  
  int Res;
  if (cbFastSubscribe->Checked)
  {
    if (SubscribeKind == skManual)
      Res = wclGattClient->UnsubscribeFromNotifications(Characteristic);
    else
    {
      Res = wclGattClient->UnsubscribeFromNotifications(Characteristic,
        OpFlag(), Protection(), SubscribeKind);
    }
  }
  else
  {
    if (SubscribeKind == skManual)
      Res = wclGattClient->Unsubscribe(Characteristic);
    else
      Res = wclGattClient->Unsubscribe(Characteristic, SubscribeKind);
  }
  
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
TwclGattOperationFlag __fastcall TfmMain::OpFlag()
{
  switch (cbOperationFlag->ItemIndex)
  {
    case 1:
      return goReadFromDevice;
    case 2:
      return goReadFromCache;
    default:
      return goNone;
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btCCCDSubscribeClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];

  TwclGattSubscribeKind SubscribeKind;
  switch (cbSubscribeKind->ItemIndex)
  {
    case 0:
      SubscribeKind = skNotification;
      break;
    case 1:
      SubscribeKind = skIndication;
      break;
    default:
      SubscribeKind = skManual;
      break;
  }

  int Res;
  if (SubscribeKind == skManual)
  {
    // In case if characteristic has both Indication and Notification properties
    // set to True we have to select one of them. Here we use Notifications but
    // you can use other one.
    if (Characteristic.IsNotifiable && Characteristic.IsIndicatable)
    {
      // Change the code line below to
      // Characteristic.IsNotifiable = false;
      // if you want to receive Indications instead of notifications.
      Characteristic.IsIndicatable = false;
    }
    Res = wclGattClient->WriteClientConfiguration(Characteristic, true,
      OpFlag(), Protection());
  }
  else
  {
    Res = wclGattClient->WriteClientConfiguration(Characteristic, true,
      OpFlag(), Protection(), SubscribeKind);
  }
  
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btCCCDUnsubscribeClick(TObject *Sender)
{
  if (lvCharacteristics->Selected == NULL)
  {
    MessageDlg("Select characteristic", mtWarning, TMsgDlgButtons() << mbOK, 0);
    return;
  }

  TwclGattCharacteristic Characteristic =
    FCharacteristics[lvCharacteristics->Selected->Index];

  TwclGattSubscribeKind SubscribeKind;
  switch (cbSubscribeKind->ItemIndex)
  {
    case 0:
      SubscribeKind = skNotification;
      break;
    case 1:
      SubscribeKind = skIndication;
      break;
    default:
      SubscribeKind = skManual;
      break;
  }

  int Res;
  if (SubscribeKind == skManual)
  {
    // In case if characteristic has both Indication and Notification properties
    // set to True we have to select one of them. Here we use Notifications but
    // you can use other one.
    // YOU MUST USE THE SAME PROPERTY THAT IS USED IN SUBSCRIBE
    if (Characteristic.IsNotifiable && Characteristic.IsIndicatable)
    {
      // Change the code line below to
      // Characteristic.IsNotifiable = false;
      // if you want to receive Indications instead of notifications.
      Characteristic.IsIndicatable = false;
    }
    Res = wclGattClient->WriteClientConfiguration(Characteristic, false,
      OpFlag(), Protection());
  }
  else
  {
    Res = wclGattClient->WriteClientConfiguration(Characteristic, false,
      OpFlag(), Protection(), SubscribeKind);
  }
  
  if (Res != WCL_E_SUCCESS)
  {
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerNumericComparison(
      TObject *Sender, const TwclBluetoothRadio *Radio,
      const __int64 Address, const DWORD Number, bool& Confirm)
{
  // Accept any pairing.
  Confirm = true;
  TraceEvent(Address, "Numeric comparison", "Number", IntToStr((int)Number));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerPasskeyNotification(
      TObject *Sender, const TwclBluetoothRadio *Radio,
      const __int64 Address, const DWORD Passkey)
{
  TraceEvent(Address, "Passkey notification", "Passkey", IntToStr((int)Passkey));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerPasskeyRequest(TObject *Sender,
      const TwclBluetoothRadio *Radio, const __int64 Address,
      DWORD& Passkey)
{
  // Use 123456 as passkey.
  Passkey = 123456;
  TraceEvent(Address, "Passkey request", "Passkey", IntToStr((int)Passkey));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerPinRequest(TObject *Sender,
      const TwclBluetoothRadio *Radio, const __int64 Address,
      String& Pin)
{
  // Use '0000' as PIN.
  Pin = "0000";
  TraceEvent(Address, "Pin request", "PIN", Pin);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerDeviceFound(TObject *Sender,
      const TwclBluetoothRadio *Radio, const __int64 Address)
{
  TListItem* Item = lvDevices->Items->Add();
  Item->Caption = IntToHex(Address, 12);
  Item->SubItems->Add(""); // We can not read a device's name here.
  Item->Data = (void*)Radio; // To use it later.

  TwclBluetoothDeviceType DevType = dtMixed;
  int Res = ((TwclBluetoothRadio*)Radio)->GetRemoteDeviceType(Address, DevType);
  if (Res != WCL_E_SUCCESS)
    Item->SubItems->Add("Error: 0x" + IntToHex(Res, 8));
  else
  {
    switch (DevType)
    {
      case dtClassic:
        Item->SubItems->Add("Classic");
        break;
      case dtBle:
        Item->SubItems->Add("BLE");
        break;
      case dtMixed:
        Item->SubItems->Add("Mixed");
        break;
      default:
        Item->SubItems->Add("Unknown");
        break;
    }
  }

  bool Paired = false;
  Res = ((TwclBluetoothRadio*)Radio)->GetRemotePaired(Address, Paired);
  if (Res != WCL_E_SUCCESS)
    Item->SubItems->Add("Error: 0x" + IntToHex(Res, 8));
  else
  {
    if (Paired)
      Item->SubItems->Add("True");
    else
      Item->SubItems->Add("False");
  }

  TraceEvent(Address, "Device found", "", "");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerDiscoveringCompleted(
      TObject *Sender, const TwclBluetoothRadio *Radio, const int Error)
{
  if (lvDevices->Items->Count == 0)
  {
    MessageDlg("No BLE devices were found.", mtInformation,
      TMsgDlgButtons() << mbOK, 0);
  }
  else
  {
    // Here we can update found devices names.
    for (int i = 0; i < lvDevices->Items->Count; i++)
    {
      TListItem* Item = lvDevices->Items->Item[i];

      __int64 Address = StrToInt64("$" + Item->Caption);
      String DevName = "";
      int Res = ((TwclBluetoothRadio*)Radio)->GetRemoteName(Address, DevName);
      if (Res != WCL_E_SUCCESS)
        Item->SubItems->Strings[0] = "Error: 0x" + IntToHex(Res, 8);
      else
        Item->SubItems->Strings[0] = DevName;
    }
  }

  TraceEvent(0, "Discovering completed", "", "");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerDiscoveringStarted(
      TObject *Sender, const TwclBluetoothRadio *Radio)
{
  lvDevices->Items->Clear();
  TraceEvent(0, "Discovering started", "", "");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattClientCharacteristicChanged(
      TObject *Sender, const WORD Handle,
      const TwclGattCharacteristicValue Value)
{
  TraceEvent(((TwclGattClient*)Sender)->Address, "ValueChanged", "Handle",
    IntToStr(Handle));
  if (Value.Length == 0)
    TraceEvent(0, "", "Value", "");

  else
  {
    String Str = "";
    for (int i = 0; i < Value.Length; i++)
      Str = Str + IntToHex(Value[i], 2);
    TraceEvent(0, "", "Value", Str);
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattClientConnect(TObject *Sender,
      const int Error)
{
  // Connection property is valid here.
  TraceEvent(((TwclGattClient*)Sender)->Address, "Connected", "Error",
    "0x" + IntToHex(Error, 8));

  if (Error == WCL_E_SUCCESS)
  {
    TraceEvent(wclGattClient->Address, "Max PDU", "", "");
    GetMaxPduSize();

    TraceEvent(wclGattClient->Address, "Connection params", "", "");
    GetConnectionParams();

    TraceEvent(wclGattClient->Address, "Connection PHY", "", "");
    GetConnectionPhy();
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattClientDisconnect(TObject *Sender,
      const int Reason)
{
  // Connection property is valid here.
  TraceEvent(((TwclGattClient*)Sender)->Address, "Disconnected", "Reason",
    "0x" + IntToHex(Reason, 8));

  Cleanup();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btTerminateClick(TObject *Sender)
{
  TwclBluetoothRadio* Radio = GetRadio();
  if (Radio != NULL)
  {
    int Res = Radio->Terminate();
    if (Res != WCL_E_SUCCESS)
    {
      MessageDlg("Error terminating discovering: 0x" + IntToHex(Res, 8),
        mtError, TMsgDlgButtons() << mbOK, 0);
    }
  }
}
//---------------------------------------------------------------------------

TwclGattProtectionLevel TfmMain::Protection()
{
  switch (cbProtection->ItemIndex)
  {
    case 0:
      return plNone;
    case 1:
      return plAuthentication;
    case 2:
      return plEncryption;
    case 3:
      return plEncryptionAndAuthentication;
    default:
      return plNone;
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclBluetoothManagerAuthenticationCompleted(
      TObject *Sender, const TwclBluetoothRadio *Radio,
      const __int64 Address, const int Error)
{
  TraceEvent(Address, "Authentication completed", "Error", IntToHex(Error, 9));
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btPairClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);

  else
  {
    TListItem* Item = lvDevices->Selected;
    int Res = ((TwclBluetoothRadio*)Item->Data)->RemotePair(StrToInt64("$" + Item->Caption),
      pmLe);
    if (Res != WCL_E_SUCCESS)
    {
      MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
        TMsgDlgButtons() << mbOK, 0);
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclBluetoothManagerConfirm(TObject *Sender,
      const TwclBluetoothRadio *Radio, const __int64 Address, bool& Confirm)
{
  // Accept any pairing.
  Confirm = true;
  TraceEvent(Address, "Just works pairing", "Accept", "True");
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::cbFastSubscribeClick(TObject *Sender)
{
  btCCCDSubscribe->Enabled = (!cbFastSubscribe->Checked);
  btCCCDUnsubscribe->Enabled = (!cbFastSubscribe->Checked);
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btGetParamsClick(TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Connection params", "", "");
  GetConnectionParams();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattClientConnectionParamsChanged(
      TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Connection params changed", "", "");
  GetConnectionParams();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btSetParamsClick(TObject *Sender)
{
  int Res;
  if (cbParams->ItemIndex == 3)
  {
    TwclBluetoothLeConnectionParametersValue ParamsValue = { 84, 84, 0, 800 };
    Res = wclGattClient->SetConnectionParams(ParamsValue);
  }
  else
  {
    TwclBluetoothLeConnectionParametersType Params;
    switch (cbParams->ItemIndex)
    {
      case 0:
        Params = ppBalanced;
        break;
      case 1:
        Params = ppPowerOptimized;
        break;
      case 2:
        Params = ppThroughputOptimized;
        break;
      default:
        throw new wclEInvalidArgument("Invalid connection parameters.");
    }
    Res = wclGattClient->SetConnectionParams(Params);
  }
  if (Res != WCL_E_SUCCESS)
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
}
//---------------------------------------------------------------------------

void TfmMain::GetConnectionParams()
{
  TwclBluetoothLeConnectionParameters Params;
  int Res = wclGattClient->GetConnectionParams(Params);
  if (Res != WCL_E_SUCCESS)
    TraceEvent(0, "", "Error", IntToHex(Res, 8));
  else
  {
    TraceEvent(0, "", "Connection interval", IntToStr(Params.Interval));
    TraceEvent(0, "", "Connection latency", IntToStr(Params.Latency));
    TraceEvent(0, "", "Link timeout", IntToStr(Params.LinkTimeout));
  }
}
//---------------------------------------------------------------------------

void TfmMain::GetMaxPduSize()
{
  unsigned short Size;
  int Res = wclGattClient->GetMaxPduSize(Size);
  if (Res != WCL_E_SUCCESS)
    TraceEvent(wclGattClient->Address, "", "Error", IntToHex(Res, 8));
  else
    TraceEvent(wclGattClient->Address, "", "Size", IntToStr(Size));
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattClientMaxPduSizeChanged(TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Max PDU size changed", "", "");
  GetMaxPduSize();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btGetMaxPduSizeClick(TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Max PDU", "", "");
  GetMaxPduSize();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btPhyClick(TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Connection PHY", "", "");
  GetConnectionPhy();
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattClientConnectionPhyChanged(TObject *Sender)
{
  TraceEvent(wclGattClient->Address, "Connection PHY changed", "", "");
  GetConnectionPhy();
}
//---------------------------------------------------------------------------

void TfmMain::GetConnectionPhy()
{
  TwclBluetoothLeConnectionPhy Phy;
  int Res = wclGattClient->GetConnectionPhyInfo(Phy);
  if (Res != WCL_E_SUCCESS)
    TraceEvent(wclGattClient->Address, "", "Error", IntToHex(Res, 8));
  else
  {
    TraceEvent(0, "Transmit PHY", "", "");
    TraceEvent(0, "", "Coded", BoolToStr(Phy.Transmit.IsCoded, true));
    TraceEvent(0, "", "1M", BoolToStr(Phy.Transmit.IsUncoded1MPhy, true));
    TraceEvent(0, "", "2M", BoolToStr(Phy.Transmit.IsUncoded2MPhy, true));

    TraceEvent(0, "Receive PHY", "", "");
    TraceEvent(0, "", "Coded", BoolToStr(Phy.Receive.IsCoded, true));
    TraceEvent(0, "", "1M", BoolToStr(Phy.Receive.IsUncoded1MPhy, true));
    TraceEvent(0, "", "2M", BoolToStr(Phy.Receive.IsUncoded2MPhy, true));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclBluetoothManagerIoCapabilityRequest(
      TObject *Sender, const TwclBluetoothRadio *Radio,
      const __int64 Address, TwclBluetoothMitmProtection& Mitm,
      TwclBluetoothIoCapability& IoCapability, bool& OobPresent)
{
  switch (cbMitmProtection->ItemIndex)
  {
    case 0:
      Mitm = mitmProtectionNotRequired;
      break;
    case 1:
      Mitm = mitmProtectionRequired;
      break;
    case 2:
      Mitm = mitmProtectionNotRequiredBonding;
      break;
    case 3:
      Mitm = mitmProtectionRequiredBonding;
      break;
    case 4:
      Mitm = mitmProtectionNotRequiredGeneralBonding;
      break;
    case 5:
      Mitm = mitmProtectionRequiredGeneralBonding;
      break;
    default:
      Mitm = mitmProtectionNotDefined;
      break;
  }

  switch (cbIoCap->ItemIndex)
  {
    case 0:
      IoCapability = iocapDisplayOnly;
      break;
    case 1:
      IoCapability = iocapDisplayYesNo;
      break;
    case 2:
      IoCapability = iocapKeyboardOnly;
      break;
    case 3:
      IoCapability = iocapNoInputNoOutput;
      break;
    case 4:
      IoCapability = iocapDisplayKeyboard;
      break;
    default:
      IoCapability = iocapNotDefined;
      break;
  }

  OobPresent = false;
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btUnpairClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    TListItem* Item = lvDevices->Selected;
    int Res = ((TwclBluetoothRadio*)Item->Data)->RemoteUnpair(StrToInt64("$" +
      Item->Caption), pmLe);
    if (Res != WCL_E_SUCCESS)
      MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btEnumPairedClick(TObject *Sender)
{
  TwclBluetoothRadio* Radio = GetRadio();
  if (Radio != NULL)
  {
    lvDevices->Items->Clear();

    TwclBluetoothAddresses Devices;
    int Res = Radio->EnumPairedDevices(dkBle, Devices);
    if (Res != WCL_E_SUCCESS)
      ShowMessage("Enum paired failed: 0x" + IntToHex(Res, 8));
    else
    {
      if (Devices.Length > 0)
      {
        for (int i = 0; i < Devices.Length; i++)
        {
          TListItem* Item = lvDevices->Items->Add();
          Item->Data = Radio;

          Item->Caption = IntToHex(Devices[i], 12);

          String Name = "";
          Res = Radio->GetRemoteName(Devices[i], Name);
          if (Res == WCL_E_SUCCESS)
            Item->SubItems->Add(Name);
          else
            Item->SubItems->Add("Error: 0x" + IntToHex(Res, 8));

          TwclBluetoothDeviceType DevType = dtMixed;
          Res = Radio->GetRemoteDeviceType(Devices[i], DevType);
          if (Res != WCL_E_SUCCESS)
            Item->SubItems->Add("Error: 0x" + IntToHex(Res, 8));
          else
          {
            switch (DevType)
            {
              case dtClassic:
                Item->SubItems->Add("Classic");
                break;
              case dtBle:
                Item->SubItems->Add("BLE");
                break;
              case dtMixed:
                Item->SubItems->Add("Mixed");
                break;
              default:
                Item->SubItems->Add("Unknown");
                break;
            }
          }

          bool Paired = false;
          Res = Radio->GetRemotePaired(Devices[i], Paired);
          if (Res != WCL_E_SUCCESS)
            Item->SubItems->Add("Error: 0x" + IntToHex(Res, 8));
          else
          {
            if (Paired)
              Item->SubItems->Add("True");
            else
              Item->SubItems->Add("False");
          }
        }
      }
    }
  }
}
//---------------------------------------------------------------------------

