/**
 * @file example.cpp
 * @brief I3C DAA API Example with Binho Supernova (Blocking C++ API)
 *
 * This example demonstrates Dynamic Address Assignment related CCC flows using:
 * - Binho Supernova
 * - Binho I3C Target Board
 * - STMicroelectronics STEVAL-MKI239A
 * - Bosch Sensortec BMI323 Shuttle Board 3.0
 *
 * Workflow:
 * 1. Connect to the adapter and read device information.
 * 2. Initialize/reset the I3C bus and execute DAA-related CCC commands:
 *    SETDASA, SETAASA, ENTDAA, and SETNEWDA.
 * 3. Read and print the target table after each DAA stage.
 */

#include <iomanip>
#include <iostream>
#include <vector>

#include "CosmicSDK.hpp"

void printTargetDeviceTable(std::vector<I3cTargetInfo> targetTable){
  for (uint8_t i = 0; i < targetTable.size(); i++) {
    std::cout << "\tStatic address:" << (int)targetTable[i].staticAddress << std::dec << std::endl;
    std::cout << "\tDynamic address: " << (int)targetTable[i].dynamicAddress << std::endl;

    std::cout << "\tPID: ";
    for (uint8_t j = 0; j < 6; ++j) {
        std::cout << (int)targetTable[i].pid[j] << " ";
    }
    std::cout << std::dec << std::endl;

    std::cout << "\tBCR: " << (int)targetTable[i].bcr.byte << std::endl;
    std::cout << "\tDCR: " << (int)targetTable[i].dcr << std::endl;
    std::cout << "\tMWL: " << (int)targetTable[i].mwl << std::endl;
    std::cout << "\tMRL: " << (int)targetTable[i].mrl << std::endl;
    std::cout << "\tMax IBI payload length: " << (int)targetTable[i].maxIbiPayloadLength << std::endl;

    std::cout << "\tTarget configuration:" << std::endl;
    std::cout << "\t\tTarget type: " << (targetTable[i].configuration.bits.targetType ? "I2C" : "I3C") << std::endl;
    std::cout << "\t\tInterrupt request: " << (targetTable[i].configuration.bits.IBIRequest ? "ACCEPT IBI" : "REJECT IBI") << std::endl;
    std::cout << "\t\tController Role request: " << (targetTable[i].configuration.bits.CRRequest ? "ACCEPT CCR" : "REJECT CCR") << std::endl;
    std::cout << "\t\tSETDASSA: " << (targetTable[i].configuration.bits.daaUseSETDASA ? "USE SETDASA" : "DO NOT USE SETDASA") << std::endl;
    std::cout << "\t\tSETDAASA: " << (targetTable[i].configuration.bits.daaUseSETAASA ? "USE SETAASA" : "DO NOT USE SETAASA") << std::endl;
    std::cout << "\t\tENTDAA: " << (targetTable[i].configuration.bits.daaUseENTDAA ? "USE ENTDAA" : "DO NOT USE ENTDAA") << std::endl;
    std::cout << "\t\tIBI Timestamp: " << (targetTable[i].configuration.bits.ibiTimestampEnable ? "ENABLE" : "DISABLE") << std::endl;
    std::cout << "\t\tPending Read capability: " << (targetTable[i].configuration.bits.pendingReadCapability ? "ENABLE" : "DISABLE") << std::endl;
    std::cout << "\t-----------------------------------------------" << std::endl;
  }
}

int main() {
  std::cout << "Starting main code" << std::endl;

  // Initialize the CosmicSDK device
  std::unique_ptr<CosmicSDK> device = std::make_unique<CosmicSDK>(2000);

  std::cout << "------------------------------------------------------------------------" << std::endl;
  std::cout << "------------------ Test connection and initialization ------------------" << std::endl;
  std::cout << "---------------------- Retrieve device information ---------------------" << std::endl;
  std::cout << "------------------------------------------------------------------------" << std::endl;

  // Connect to the device
  if (!device->connect()) {
    std::cerr << "Failed to connect to a Binho Device!" << std::endl;
    exit(1);
  }

  // Variable to hold the retrieved device information
  CosmicSDK::DeviceInfo deviceInformation;
  // Read device information
  if (device->getDeviceInfo(deviceInformation) != SUCCESS) {
    std::cerr << "Failed to read device information!" << std::endl;
    exit(1);
  }

  std::cerr << "Manufacturer: " << deviceInformation.manufacturer << std::endl;
  std::cerr << "Product name: " << deviceInformation.productName << std::endl;
  std::cerr << "Serial number: " << deviceInformation.serialNumber << std::endl;
  std::cerr << "FW version: " << deviceInformation.fwVersion << std::endl;
  std::cerr << "HW version: " << deviceInformation.hwVersion << std::endl;
  std::cout << "\n" << std::endl;

  // Init I3C bus
  std::cout << "Initializing I3C bus: ";
  uint16_t result_code = device->i3cInitBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to init the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

  std::cout << "------------------------------------------------------------------------" << std::endl;
  std::cout << "------------------------------- SETDASA --------------------------------" << std::endl;
  std::cout << "------------------------------------------------------------------------" << std::endl;

  // Reset I3C bus
  std::cout << "Resetting I3C bus: ";
  result_code = device->i3cResetBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to reset the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

   // Sending SetDASA
  std::cout << "Sending SetDASA (static_addr 0x69, new_dynamic_addr 0x0B): ";
  result_code = device->i3cCccSETDASA(0x69, 0x0B);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to SetDASA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Sending SetDASA
  std::cout << "Sending SetDASA (static_addr 0x6B, new_dynamic_addr 0x0C): ";
  result_code = device->i3cCccSETDASA(0x6B, 0x0C);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to SetDASA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Get I3C target devices table
  std::cout << "Getting I3C target devices table: ";
  static std::vector<I3cTargetInfo> targetTable;
  result_code = device->i3cGetDeviceTable(targetTable);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to get the I3C target devices table! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;
  printTargetDeviceTable(targetTable);
  
  std::cout << "------------------------------------------------------------------------" << std::endl;
  std::cout << "------------------------------- SETAASA --------------------------------" << std::endl;
  std::cout << "------------------------------------------------------------------------" << std::endl;

  // Reset I3C bus
  std::cout << "Resetting I3C bus: ";
  result_code = device->i3cResetBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to reset the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

  // Sending SetAASA
  std::cout << "Sending SetAASA: ";
  const std::vector<uint8_t> staticAddresses = {0x69, 0x6B};
  result_code = device->i3cCccSETAASA(staticAddresses);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to SetAASA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Get I3C target devices table
  std::cout << "Getting I3C target devices table: ";
  result_code = device->i3cGetDeviceTable(targetTable);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to get the I3C target devices table! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;
  printTargetDeviceTable(targetTable);

  std::cout << "------------------------------------------------------------------------" << std::endl;
  std::cout << "-------------------------------- ENTDAA --------------------------------" << std::endl;
  std::cout << "------------------------------------------------------------------------" << std::endl;

  // Reset I3C bus
  std::cout << "Resetting I3C bus: ";
  result_code = device->i3cResetBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to reset the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

  // Sending ENTDAA
  std::cout << "Sending ENTDAA: ";
  result_code = device->i3cCccENTDAA();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to ENTDAA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Get I3C target devices table
  std::cout << "Getting I3C target devices table: ";
  result_code = device->i3cGetDeviceTable(targetTable);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to get the I3C target devices table! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;
  printTargetDeviceTable(targetTable);

  std::cout << "------------------------------------------------------------------------" << std::endl;
  std::cout << "------------------------------- SETNEWDA -------------------------------" << std::endl;
  std::cout << "------------------------------------------------------------------------" << std::endl;

  // Reset I3C bus
  std::cout << "Resetting I3C bus: ";
  result_code = device->i3cResetBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to reset the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

  // Init I3C bus
  std::cout << "Initializing I3C bus: ";
  result_code = device->i3cInitBus();
  if (result_code != SUCCESS) {
    std::cerr << "Failed to init the I3C bus! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;

  // Sending SetNewDA
  std::cout << "Sending SetNewDA (old_dynamic_addr 0x08, new_dynamic_addr 0x0B): ";
  result_code = device->i3cCccSETNEWDA(0x08, 0x0B);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to SetNewDA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Sending SetNewDA
  std::cout << "Sending SetNewDA (old_dynamic_addr 0x09, new_dynamic_addr 0x0C): ";
  result_code = device->i3cCccSETNEWDA(0x09, 0x0C);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to SetNewDA! ERROR CODE: " << result_code << std::endl;
  }
  printf("OK \n");

  // Get I3C target devices table
  std::cout << "Getting I3C target devices table: ";
  result_code = device->i3cGetDeviceTable(targetTable);
  if (result_code != SUCCESS) {
    std::cerr << "Failed to get the I3C target devices table! ERROR CODE:" << result_code << std::endl;
    exit(1);
  }
  std::cout << "OK" << std::endl;
  printTargetDeviceTable(targetTable);

  // Disconnect the device
  device->disconnect();

  std::cout << "Finished example!" << std::endl;
}
