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

#include <vcl.h>
#pragma hdrstop

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

#ifdef _WIN64
#pragma comment(lib, "wclCommon.a")
#pragma comment(lib, "wclCommunication.a")
#pragma comment(lib, "wclBluetoothFrameworkR.a")
#else
#pragma comment(lib, "wclCommon.lib")
#pragma comment(lib, "wclCommunication.lib")
#pragma comment(lib, "wclBluetoothFrameworkR.lib")
#endif

TfmMain *fmMain;
//---------------------------------------------------------------------------
__fastcall TfmMain::TfmMain(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerAfterOpen(TObject *Sender)
{
  lbLog->Items->Add("Bluetooth Manager has been opened");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerBeforeClose(TObject *Sender)
{
  lbLog->Items->Add("Bluetooth Manager is closing");        
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerStarted(TObject *Sender)
{
  lbLog->Items->Add("Server has been started");
  FStarted = true;
  FCounter = 0;
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerStopped(TObject *Sender)
{
  lbLog->Items->Add("Server has been stopped");
  FStarted = false;
}
//---------------------------------------------------------------------------

bool TfmMain::InitBluetooth(TwclBluetoothRadio*& Radio)
{
  Radio = NULL;

  bool Result = false;

  int Res = wclBluetoothManager->Open();
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Bluetooth Manager open failed: 0x" + IntToHex(Res, 8));
  else
  {
    lbLog->Items->Add("Checking for working Radio");
    Res = wclBluetoothManager->GetLeRadio(Radio);
    if (Res != WCL_E_SUCCESS)
      lbLog->Items->Add("Get workign radio failed: 0x" + IntToHex(Res, 8));
    else
    {
      lbLog->Items->Add("Use " + Radio->ApiName + " radio");
      Result = true;
    }

    if (!Result)
      wclBluetoothManager->Close();
  }

  return Result;
}
//---------------------------------------------------------------------------

void TfmMain::UninitBluetooth()
{
  lbLog->Items->Add("Close Bluetooth manager");
  if (wclBluetoothManager->Active)
    wclBluetoothManager->Close();
}
//---------------------------------------------------------------------------

bool TfmMain::InitServer(TwclBluetoothRadio* Radio)
{
  lbLog->Items->Add("Initialize server");
  int Res = wclGattServer->Initialize(Radio);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Initialize server failed: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Server initialized");
  return true;
}
//---------------------------------------------------------------------------

void TfmMain::UninitServer()
{
  lbLog->Items->Add("Uninitializing server");
  int Res = wclGattServer->Uninitialize();
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Uninitialize server failed: 0x" + IntToHex(Res, 8));
}
//---------------------------------------------------------------------------

bool TfmMain::AddCharacteristics(TwclGattLocalService* Service)
{
  lbLog->Items->Add("Adding characteristics services");
  TwclGattUuid Uuid;
  Uuid.IsShortUuid = true;

  TwclGattLocalCharacteristicParameters Params;
  TwclGattLocalCharacteristic* Char;

  Params.Descriptors.Length = 0;
  Params.PresentationFormats.Length = 0;

  lbLog->Items->Add("Add empty characteristic");
  Uuid.ShortUuid = 0xFFF1;
  Params.Props.Clear();
  Params.UserDescription = "Empty characteristic";
  int Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add empty characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Add short readable characteristic");
  Uuid.ShortUuid = 0xFFF2;
  Params.Props.Clear();
  Params.Props << cpReadable;
  Params.UserDescription = "Short readable characteristic";
  Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add short readable characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Add long readable characteristic");
  Uuid.ShortUuid = 0xFFF3;
  Params.Props.Clear();
  Params.Props << cpReadable;
  Params.UserDescription = "Long readable characteristic";
  Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add long readable characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Add writable characteristic");
  Uuid.ShortUuid = 0xFFF4;
  Params.Props.Clear();
  Params.Props << cpWritable << cpWritableWithoutResponse;
  Params.UserDescription = "Writable characteristic";
  Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add writable characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Add notifiable characteristic");
  Uuid.ShortUuid = 0xFFF5;
  Params.Props.Clear();
  Params.Props << cpNotifiable;
  Params.UserDescription = "Notifiable characteristic";
  Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add notifiable characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Add indicatable characteristic");
  Uuid.ShortUuid = 0xFFF6;
  Params.Props.Clear();
  Params.Props << cpIndicatable;
  Params.UserDescription = "Indicatable characteristic";
  Res = Service->AddCharacteristic(Uuid, Params, Char);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add indicatable characteristic: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("All charactertistics were added");
  return true;
}
//---------------------------------------------------------------------------

bool TfmMain::AddServices()
{
  lbLog->Items->Add("Preparing services");
  TwclGattUuid Uuid;
  Uuid.IsShortUuid = true;
  Uuid.ShortUuid = 0xFFF0;

  lbLog->Items->Add("Add service");
  TwclGattLocalService* Service;
  int Res = wclGattServer->AddService(Uuid, Service);
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("Failed to add service: 0x" + IntToHex(Res, 8));
    return false;
  }

  lbLog->Items->Add("Service has been added");
  return AddCharacteristics(Service);
}
//---------------------------------------------------------------------------

void TfmMain::Start()
{
  if (FStarted)
    lbLog->Items->Add("Already started");
  else
  {
    TwclBluetoothRadio* Radio = NULL;
    if (InitBluetooth(Radio))
    {
      bool Result = InitServer(Radio);
      if (Result)
      {
        Result = AddServices();
        if (Result)
        {
          lbLog->Items->Add("Starting server");
          int Res = wclGattServer->Start();
          if (Res != WCL_E_SUCCESS)
          {
            lbLog->Items->Add("Start server failed: 0x" + IntToHex(Res, 8));
            Result = false;
          }
        }
        if (!Result)
          UninitServer();
      }
      if (!Result)
        UninitBluetooth();
    }
  }
}
//---------------------------------------------------------------------------

void TfmMain::Stop()
{
  if (!FStarted)
    lbLog->Items->Add("Not started");
  else
  {
    lbLog->Items->Add("Stopping server");
    int Res = wclGattServer->Stop();
    if (Res != WCL_E_SUCCESS)
      lbLog->Items->Add("Stop server failed: 0x" + IntToHex(Res, 8));

    UninitServer();
    UninitBluetooth();
  }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btStartClick(TObject *Sender)
{
  Start();        
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btStopClick(TObject *Sender)
{
  Stop();        
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::FormDestroy(TObject *Sender)
{
  Stop();        
}
//---------------------------------------------------------------------------


void __fastcall TfmMain::wclGattServerSubscribed(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic)
{
  String Str = "Client subscribed: " + IntToHex(Client->Address, 12);

  unsigned short Size;
  int Res = ((TwclGattServerClient*)Client)->GetMaxNotificationSize(Size);
  if (Res != WCL_E_SUCCESS)
    Str = Str + " Get max notification size failed: 0x" + IntToHex(Res, 8);
  else
    Str = Str + " Max notification size: " + IntToStr(Size);
  lbLog->Items->Add(Str);

  Res = ((TwclGattLocalCharacteristic*)Characteristic)->Notify(Client->Address,
    &FCounter, 4);
  FCounter++;
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Notification failed: " + IntToHex(Res, 8));
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerUnsubscribed(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic)
{
  lbLog->Items->Add("Client unsubscribed :" + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------


void __fastcall TfmMain::wclGattServerClientConnected(TObject *Sender,
      const TwclGattServerClient *Client)
{
  lbLog->Items->Add("Client connected :" + IntToHex(Client->Address, 12));

  unsigned short Size;
  int Res = ((TwclGattServerClient*)Client)->GetMaxPduSize(Size);
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Get max PDU size failed: 0x" + IntToHex(Res, 8));
  else
    lbLog->Items->Add("Max PDU size: " + IntToStr(Size));

  GetConParams(Client);
  GetConPhy(Client);
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerClientDisconnected(TObject *Sender,
      const TwclGattServerClient *Client)
{
  lbLog->Items->Add("Client disconnected :" + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerNotificationSizeChanged(
      TObject *Sender, const TwclGattServerClient *Client)
{
  unsigned short Size;
  int Res = ((TwclGattServerClient*)Client)->GetMaxNotificationSize(Size);
  if (Res == WCL_E_SUCCESS)
  {
    lbLog->Items->Add(IntToHex(Client->Address, 12) +
      " notification size changed: " + IntToStr(Size));
  }
  else
  {
    lbLog->Items->Add(IntToHex(Client->Address, 12) +
      " get max notification size failed: 0x" + IntToHex(Res, 8));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerMaxPduSizeChanged(TObject *Sender,
      const TwclGattServerClient *Client)
{
  unsigned short Size;
  int Res = ((TwclGattServerClient*)Client)->GetMaxPduSize(Size);
  if (Res == WCL_E_SUCCESS)
  {
    lbLog->Items->Add(IntToHex(((TwclGattServerClient*)Client)->Address, 12) +
      " PDU size changed: " + IntToStr(Size));
  }
  else
  {
    lbLog->Items->Add(IntToHex(((TwclGattServerClient*)Client)->Address, 12) +
      " get max PDU size failed: 0x" + IntToHex(Res, 8));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerRead(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic,
      const TwclGattLocalCharacteristicReadRequest *Request)
{
  lbLog->Items->Add("Read request from " + IntToHex(Client->Address, 12));
  lbLog->Items->Add("  Offset: " + IntToStr((int)Request->Offset));
  lbLog->Items->Add("  Read Buffer Size: " + IntToStr((int)Request->Size));

  int Res;
  bool IsShort = (Characteristic->Uuid.IsShortUuid &&
    Characteristic->Uuid.ShortUuid == 0xFFF2);
  if (IsShort)
  {
    Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->Respond(
      FShortData, SHORT_DATA_LEN);
  }
  else
  {
    Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->Respond(
      &FLongData[Request->Offset], LONG_DATA_LEN - Request->Offset);
  }
  if (Res != WCL_E_SUCCESS)
  {
    lbLog->Items->Add("  Set data failed: 0x" + IntToHex(Res, 8));
    lbLog->Items->Add("  respond with error");
    Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->RespondWithError(
      WCL_E_BLUETOOTH_LE_UNLIKELY);
    if (Res != WCL_E_SUCCESS)
      lbLog->Items->Add("  Respond failed: 0x" + IntToHex(Res, 8));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerWrite(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic,
      const TwclGattLocalCharacteristicWriteRequest *Request)
{
  lbLog->Items->Add("Data received from " + IntToHex(Client->Address, 12));
  lbLog->Items->Add("  Size: " + IntToStr((int)Request->Size));
  lbLog->Items->Add("  Offset: " + IntToStr((int)Request->Offset));

  String s = "";
  for (DWORD i = 0; i < Request->Size; i++)
    s = s + IntToHex(((System::PByte)Request->Data)[i], 2);
  lbLog->Items->Add(s);

  if (Request->WithResponse)
  {
    lbLog->Items->Add("  Write With Response. Sending response");
    int Res = ((TwclGattLocalCharacteristicWriteRequest*)Request)->Respond();
    if (Res != WCL_E_SUCCESS)
      lbLog->Items->Add("  Respond failed: 0x" + IntToHex(Res, 8));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::FormCreate(TObject *Sender)
{
  FStarted = false;
  for (WORD i = 0; i < SHORT_DATA_LEN; i++)
    FShortData[i] = i + 1;
  for (WORD i = 0; i < LONG_DATA_LEN; i++)
    FLongData[i] = LOBYTE(i);
}
//---------------------------------------------------------------------------

void TfmMain::GetConParams(const TwclGattServerClient* Client)
{
  TwclBluetoothLeConnectionParameters Params;
  int Res = ((TwclGattServerClient*)Client)->GetConnectionParams(Params);
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Get connection params failed: 0x" + IntToHex(Res, 8));

  else
  {
    lbLog->Items->Add("Connection params [" + IntToHex(Client->Address, 12) + "]");
    lbLog->Items->Add("  Interval     : " + IntToStr(Params.Interval));
    lbLog->Items->Add("  Latency      : " + IntToStr(Params.Latency));
    lbLog->Items->Add("  Link Timeout : " + IntToStr(Params.LinkTimeout));
  }
}
//---------------------------------------------------------------------------

void TfmMain::GetConPhy(const TwclGattServerClient* Client)
{
  TwclBluetoothLeConnectionPhy Phy;
  int Res = ((TwclGattServerClient*)Client)->GetConnectionPhyInfo(Phy);
  if (Res != WCL_E_SUCCESS)
    lbLog->Items->Add("Get connection PHY failed: 0x" + IntToHex(Res, 8));

  else
  {
    lbLog->Items->Add("Connection PHY [" + IntToHex(Client->Address, 12) + "]");
    lbLog->Items->Add("  Receive");
    lbLog->Items->Add("    IsCoded        : " + BoolToStr(Phy.Receive.IsCoded));
    lbLog->Items->Add("    IsUncoded1MPhy : " + BoolToStr(Phy.Receive.IsUncoded1MPhy));
    lbLog->Items->Add("    IsUncoded2MPhy : " + BoolToStr(Phy.Receive.IsUncoded2MPhy));
    lbLog->Items->Add("  Transmit");
    lbLog->Items->Add("    IsCoded        : " + BoolToStr(Phy.Transmit.IsCoded));
    lbLog->Items->Add("    IsUncoded1MPhy : " + BoolToStr(Phy.Transmit.IsUncoded1MPhy));
    lbLog->Items->Add("    IsUncoded2MPhy : " + BoolToStr(Phy.Transmit.IsUncoded2MPhy));
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerConnectionParamsChanged(
      TObject *Sender, const TwclGattServerClient *Client)
{
  GetConParams(Client);
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::wclGattServerConnectionPhyChanged(TObject *Sender,
      const TwclGattServerClient *Client)
{
  GetConPhy(Client);
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::btClearClick(TObject *Sender)
{
  lbLog->Items->Clear();
}
//---------------------------------------------------------------------------

