/**
 * @file example.c
 * @brief I3C CCC API Example with Binho Supernova (Blocking C API)
 *
 * This example demonstrates Common Command Code (CCC) operations over I3C using:
 * - Binho Supernova
 * - Binho I3C Target Board
 * - STMicroelectronics STEVAL-MKI239A
 * - Bosch Sensortec BMI323 Shuttle Board 3.0
 *
 * Workflow:
 * 1. Connect to the adapter and print host device information.
 * 2. Set I3C VTARG, initialize the controller, and initialize the I3C bus.
 * 3. Retrieve target table and execute representative CCC commands:
 *    GETPID/GETBCR/GETDCR/GETCAPS, GET/SET MRL, GET/SET MWL,
 *    ENEC/DISEC, RSTACT, target reset pattern, and HDR exit pattern.
 * 4. Print returned data from CCC reads.
 */

#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");

  // Set I3C bus voltage and parameters
  printf("Setting voltage to 3.3v: ");
  result_code = CosmicSDK_I3cSetBusVoltage(handle_ptr, 3300);
  if (result_code != SUCCESS) {
    printf("Failed to set I3C bus voltage! \n ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Init I3C peripheral
  printf("Initializing I3C controller: ");
  result_code = CosmicSDK_I3cControllerInit(handle_ptr, PUSH_PULL_10_MHz_DC_40, OPEN_DRAIN_400_KHz, FAST_MODE, FAST);
  if (result_code != SUCCESS && result_code != FW_INTERFACE_ALREADY_INITIALIZED) {
    printf("Failed to init I3C controller! 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");

  // 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("----------------------------- CCC Examples ------------------------------ \n");
  printf("------------------------------------------------------------------------- \n");
  
  // Get PID
  printf("Getting PID: ");
  uint8_t pid[6];
  result_code = CosmicSDK_I3cCccGETPID(handle_ptr, 0x08, pid);
  if (result_code != SUCCESS) {
    printf("Failed to obtain PID! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tPID: "); 
  for (uint8_t i = 0; i < 6; ++i) { 
    printf("%d ", pid[i]); 
  } 
  printf("\n");

  // Get BCR
  printf("Getting BCR: ");
  uint8_t bcr;
  result_code = CosmicSDK_I3cCccGETBCR(handle_ptr, 0x08, &bcr);
  if (result_code != SUCCESS) {
    printf("Failed to obtain BCR! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tBCR: "); 
  printf("%d ", bcr); 
  printf("\n");

  // Get DCR
  printf("Getting DCR: ");
  uint8_t dcr;
  result_code = CosmicSDK_I3cCccGETDCR(handle_ptr, 0x08, &dcr);
  if (result_code != SUCCESS) {
    printf("Failed to obtain DCR! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tDCR: "); 
  printf("%d ", dcr); 
  printf("\n");

  // Get CAPS
  printf("Getting CAPS: ");
  uint8_t caps[4];
  uint8_t lenOut;
  result_code = CosmicSDK_I3cCccGETCAPS(handle_ptr, 0x08, caps, &lenOut, false, 0);
  if (result_code != SUCCESS) {
    printf("Failed to obtain CAPS! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tCAPS: "); 
  for (uint8_t i = 0; (i < lenOut)&&(i < 4); ++i) { 
    printf("%d ", caps[i]); 
  } 
  printf("\n");

  // Get MRL
  printf("Getting MRL 0x09: ");
  uint16_t mrl;
  uint8_t ibiLength;
  bool ibiLengthReturned;
  result_code = CosmicSDK_I3cCccGETMRL(handle_ptr, 0x09, &mrl, &ibiLength, &ibiLengthReturned);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMRL: %d \n", mrl);
  printf("\tIBI Length: ");
  if (ibiLengthReturned)
      printf("%u\n", ibiLength);
  else
      printf("None\n");

  // Set broadcast MRL
  printf("Setting broadcast MRL=0x0F03: ");
  mrl = 0x0F03;
  ibiLength = 0x03;
  result_code = CosmicSDK_I3cCccSETMRLBroadcast(handle_ptr, mrl, true, ibiLength);
  if (result_code != SUCCESS) {
    printf("Failed to set MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get MRL
  printf("Getting MRL 0x08: ");
  ibiLength = 0;
  result_code = CosmicSDK_I3cCccGETMRL(handle_ptr, 0x08, &mrl, &ibiLength, &ibiLengthReturned);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMRL: %d \n", mrl);
  printf("\tIBI Length: ");
  if (ibiLengthReturned)
      printf("%u\n", ibiLength);
  else
      printf("None\n");
  // Get MRL
  printf("Getting MRL 0x09: ");
  result_code = CosmicSDK_I3cCccGETMRL(handle_ptr, 0x09, &mrl, NULL, NULL);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMRL: %d \n", mrl);
  printf("\tIBI Length: None\n");

  // Set direct MRL
  printf("Setting direct MRL=0x0200 to 0x09: ");
  mrl = 0x0200;
  result_code = CosmicSDK_I3cCccSETMRLDirect(handle_ptr, 0x09, mrl, false, 0);
  if (result_code != SUCCESS) {
    printf("Failed to set MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

    // Get MRL
  printf("Getting MRL 0x08: ");
  result_code = CosmicSDK_I3cCccGETMRL(handle_ptr, 0x08, &mrl, &ibiLength, &ibiLengthReturned);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMRL: %d \n", mrl);
  printf("\tIBI Length: ");
  if (ibiLengthReturned)
      printf("%u\n", ibiLength);
  else
      printf("None\n");
  // Get MRL
  printf("Getting MRL 0x09: ");
  result_code = CosmicSDK_I3cCccGETMRL(handle_ptr, 0x09, &mrl, &ibiLength, &ibiLengthReturned);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MRL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMRL: %d \n", mrl);
  printf("\tIBI Length: ");
  if (ibiLengthReturned)
      printf("%u\n", ibiLength);
  else
      printf("None\n");

  // Get MWL
  printf("Getting MWL 0x09: ");
  uint16_t mwl;
  result_code = CosmicSDK_I3cCccGETMWL(handle_ptr, 0x09, &mwl);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMWL: %d \n", mwl);

  // Set broadcast MWL
  printf("Setting broadcast MWL=0x0F03: ");
  mwl = 0x0F03;
  result_code = CosmicSDK_I3cCccSETMWLBroadcast(handle_ptr, mwl);
  if (result_code != SUCCESS) {
    printf("Failed to set MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Get MWL
  printf("Getting MWL 0x08: ");
  result_code = CosmicSDK_I3cCccGETMWL(handle_ptr, 0x08, &mwl);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMWL: %d \n", mwl);
  // Get MWL
  printf("Getting MWL 0x09: ");
  result_code = CosmicSDK_I3cCccGETMWL(handle_ptr, 0x09, &mwl);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMWL: %d \n", mwl);

  // Set direct MWL
  printf("Setting direct MWL=0x0200 to 0x08: ");
  mwl = 0x0200;
  result_code = CosmicSDK_I3cCccSETMWLDirect(handle_ptr, 0x08, mwl);
  if (result_code != SUCCESS) {
    printf("Failed to set MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

    // Get MWL
  printf("Getting MWL 0x08: ");
  result_code = CosmicSDK_I3cCccGETMWL(handle_ptr, 0x08, &mwl);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMWL: %d \n", mwl);

  // Get MWL
  printf("Getting MWL 0x09: ");
  result_code = CosmicSDK_I3cCccGETMWL(handle_ptr, 0x09, &mwl);
  if (result_code != SUCCESS) {
    printf("Failed to obtain MWL! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tMWL: %d \n", mwl);

  // Broadcast DISEC
  printf("Sending CCC broadcast DISEC: ");
  result_code = CosmicSDK_I3cCccDISECBroadcast(handle_ptr, IBI | CR | HJ);
  if (result_code != SUCCESS) {
    printf("Failed to perform broadcast DISEC! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  
  // Direct DISEC
  printf("Sending CCC Direct DISEC: ");
  result_code = CosmicSDK_I3cCccDISECDirect(handle_ptr, 0x08, CR | HJ);
  if (result_code != SUCCESS) {
    printf("Failed to perform Direct DISEC! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Broadcast ENEC
  printf("Sending CCC broadcast ENEC: ");
  result_code = CosmicSDK_I3cCccENECBroadcast(handle_ptr, IBI | CR | HJ);
  if (result_code != SUCCESS) {
    printf("Failed to perform broadcast ENEC! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  
  // Direct ENEC
  printf("Sending CCC Direct ENEC: ");
  result_code = CosmicSDK_I3cCccENECDirect(handle_ptr, 0x08, CR | HJ);
  if (result_code != SUCCESS) {
    printf("Failed to perform Direct ENEC! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Direct RSTACT
  printf("Sending CCC Direct RSTACT: ");
  uint8_t outValue;
  result_code = CosmicSDK_I3cCccRSTACTDirect(handle_ptr, 0x08, NO_RESET, READ, &outValue);
  if (result_code != SUCCESS) {
    printf("Failed to perform Direct RSTACT! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");
  printf("\tData: %u\n", outValue);
    
  // Broadcast RSTACT
  printf("Sending CCC Broadcast RSTACT: ");
  result_code = CosmicSDK_I3cCccRSTACTBroadcast(handle_ptr, NO_RESET);
  if (result_code != SUCCESS) {
    printf("Failed to perform Broadcast RSTACT! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Direct RSTACT
  printf("Sending CCC Direct RSTACT: ");
  result_code = CosmicSDK_I3cCccRSTACTDirect(handle_ptr, 0x08, RESET_WHOLE_TARGET, WRITE, &outValue);
  if (result_code != SUCCESS) {
    printf("Failed to perform Direct RSTACT! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  // Send Reset Pattern
  printf("Sending Reset Pattern: ");
  result_code = CosmicSDK_I3cTargetResetPattern(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to reset pattern! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }

  // Send Exit Pattern
  printf("Sending Exit Pattern: ");
  result_code = CosmicSDK_I3cHdrExitPattern(handle_ptr);
  if (result_code != SUCCESS) {
    printf("Failed to exit pattern! ERROR CODE: %u \n", result_code);
    exit_code = 1;
    goto cleanup;
  }
  printf("OK \n");

  printf("Finished example! \n");

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