/**
 * @file example.c
 * @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 <stddef.h>  // for size_t
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "CosmicSDK_API_c.h"

#define CHECK_RESULT(op, code) \
  do { \
    if ((code) != SUCCESS) { \
      printf("%s failed. ERROR CODE: %u\n", (op), (unsigned)(code)); \
      exit_code = 1; \
      goto cleanup; \
    } \
  } while (0)

static void printTargetDeviceTable(const I3cTargetInfo* targetTable, uint8_t targetsQuantity) {
  for (uint8_t i = 0; i < targetsQuantity; i++) { 
    printf("\tStatic address: 0x%02X\n", targetTable[i].staticAddress); 
    printf("\tDynamic address: 0x%02X\n", targetTable[i].dynamicAddress); 

    printf("\tPID: "); 
    for (uint8_t j = 0; j < 6; ++j) { 
      printf("%02X ", targetTable[i].pid[j]); 
    } 
    printf("\n"); 

    printf("\tBCR: 0x%02X\n", targetTable[i].bcr.byte); 
    printf("\tDCR: 0x%02X\n", targetTable[i].dcr); 
    printf("\tMWL: %u\n", targetTable[i].mwl); 
    printf("\tMRL: %u\n", targetTable[i].mrl); 
    printf("\tMax IBI payload length: %u\n", targetTable[i].maxIbiPayloadLength); 

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

int main(void) {
  int exit_code = 0;
  bool connected = false;
  CosmicSDK_Handle* handle_ptr = NULL;
  uint16_t result_code = SUCCESS;
  printf("Started C example \n");

  printf("------------------------------------------------------------------------ \n");
  printf("------------------ Test connection and initialization ------------------ \n");
  printf("---------------------- Retrieve device information --------------------- \n");
  printf("------------------------------------------------------------------------ \n");

  handle_ptr = CosmicSDK_Create(2000);
  if (handle_ptr == NULL) {
    printf("Could not create CosmicSDK handle\n");
    exit_code = 1;
    goto cleanup;
  }

  if (!CosmicSDK_Connect(handle_ptr, NULL)) {
    printf("Could not connect to device\n");
    exit_code = 1;
    goto cleanup;
  }
  connected = true;

  C_DeviceInfo deviceInfoOut;
  result_code = CosmicSDK_GetDeviceInfo(handle_ptr, &deviceInfoOut);
  CHECK_RESULT("GetDeviceInfo", result_code);
  printf("DEVICE INFO:\n");
  printf("Manufacturer: %.*s\n", (int)deviceInfoOut.manufacturerLength, deviceInfoOut.manufacturer);
  printf("Product Name: %.*s\n", (int)deviceInfoOut.productNameLength, deviceInfoOut.productName);
  printf("Serial Number: %.*s\n", (int)deviceInfoOut.serialNumberLength, deviceInfoOut.serialNumber);
  printf("Firmware Version: %.*s\n", (int)deviceInfoOut.fwVersionLength, deviceInfoOut.fwVersion);
  printf("Hardware Version: %.*s\n", (int)deviceInfoOut.hwVersionLength, deviceInfoOut.hwVersion);
  printf("\n");

  // Init I3C bus
  printf("Initializing I3C bus: ");
  result_code = CosmicSDK_I3cInitBus(handle_ptr, NULL, 0);
  if (result_code != SUCCESS) {
    printf("Failed to init I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  printf("------------------------------------------------------------------------ \n");
  printf("------------------------------- SETDASA -------------------------------- \n");
  printf("------------------------------------------------------------------------ \n");

  // Reset I3C bus
  printf("Resetting I3C bus: ");
  result_code = CosmicSDK_I3cResetBus(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to reset I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC SetDASA
  printf("Sending SetDASA (static_adr 0x69; dynamic_addr 0x0B): ");
  result_code = CosmicSDK_I3cCccSETDASA(handle_ptr, 0x69, 0x0B);
  if (result_code != SUCCESS) {
    printf("Failed to SetDASA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC SetDASA
  printf("Sending SetDASA (static_adr 0x6B; dynamic_addr 0x0C): ");
  result_code = CosmicSDK_I3cCccSETDASA(handle_ptr, 0x6B, 0x0C);
  if (result_code != SUCCESS) {
    printf("Failed to SetDASA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get I3C target devices table
  printf("Getting I3C target devices table: ");
  I3cTargetInfo targetTable[5];
  uint8_t maxTargets = 5;
  uint8_t targetsQuantity;
  result_code = CosmicSDK_I3cGetDeviceTable(handle_ptr, targetTable, maxTargets, &targetsQuantity);
  if (result_code != SUCCESS) {
    printf("Failed to get the I3C target devices table! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printTargetDeviceTable(targetTable, targetsQuantity);

  printf("------------------------------------------------------------------------ \n");
  printf("------------------------------- SETAASA -------------------------------- \n");
  printf("------------------------------------------------------------------------ \n");

  // Reset I3C bus
  printf("Resetting I3C bus: ");
  result_code = CosmicSDK_I3cResetBus(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to reset I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC SetAASA
  printf("Sending SetAASA: ");
  uint8_t staticAddresses[] = {0x69, 0x6B};
  result_code = CosmicSDK_I3cCccSETAASA(handle_ptr, staticAddresses, 2);
  if (result_code != SUCCESS) {
    printf("Failed to SetAASA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get I3C target devices table
  printf("Getting I3C target devices table: ");
  result_code = CosmicSDK_I3cGetDeviceTable(handle_ptr, targetTable, maxTargets, &targetsQuantity);
  if (result_code != SUCCESS) {
    printf("Failed to get the I3C target devices table! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printTargetDeviceTable(targetTable, targetsQuantity);

  printf("------------------------------------------------------------------------ \n");
  printf("-------------------------------- ENTDAA -------------------------------- \n");
  printf("------------------------------------------------------------------------ \n");

  // Reset I3C bus
  printf("Resetting I3C bus: ");
  result_code = CosmicSDK_I3cResetBus(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to reset I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC ENTDAA
  printf("Sending ENTDAA: ");
  result_code = CosmicSDK_I3cCccENTDAA(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to ENTDAA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get I3C target devices table
  printf("Getting I3C target devices table: ");
  result_code = CosmicSDK_I3cGetDeviceTable(handle_ptr, targetTable, maxTargets, &targetsQuantity);
  if (result_code != SUCCESS) {
    printf("Failed to get the I3C target devices table! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printTargetDeviceTable(targetTable, targetsQuantity);

  printf("------------------------------------------------------------------------ \n");
  printf("------------------------------- SETNEWDA ------------------------------- \n");
  printf("------------------------------------------------------------------------ \n");

  // Reset I3C bus
  printf("Resetting I3C bus: ");
  result_code = CosmicSDK_I3cResetBus(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to reset I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Init I3C bus
  printf("Initializing I3C bus: ");
  result_code = CosmicSDK_I3cInitBus(handle_ptr, NULL, 0);
  if (result_code != SUCCESS) {
    printf("Failed to init I3C bus! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC SetNewDA
  printf("Sending SetNewDA (old_dynamic_addr 0x08; dynamic_addr 0x0B): ");
  result_code = CosmicSDK_I3cCccSETNEWDA(handle_ptr, 0x08, 0x0B);
  if (result_code != SUCCESS) {
    printf("Failed to SetNewDA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // CCC SetNewDA
  printf("Sending SetNewDA (old_dynamic_addr 0x09; dynamic_addr 0x0C): ");
  result_code = CosmicSDK_I3cCccSETNEWDA(handle_ptr, 0x09, 0x0C);
  if (result_code != SUCCESS) {
    printf("Failed to SetNewDA! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get I3C target devices table
  printf("Getting I3C target devices table: ");
  result_code = CosmicSDK_I3cGetDeviceTable(handle_ptr, targetTable, maxTargets, &targetsQuantity);
  if (result_code != SUCCESS) {
    printf("Failed to get the I3C target devices table! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printTargetDeviceTable(targetTable, targetsQuantity);

  printf("Finished example! \n");

cleanup:
  if (connected) {
    CosmicSDK_Disconnect(handle_ptr);
  }
  if (handle_ptr != NULL) {
    CosmicSDK_Destroy(handle_ptr);
  }
  return exit_code;
}
