/**
 * @file example.c
 * @brief I2C EEPROM Example with Binho Supernova/Pulsar (Blocking C API)
 *
 * This example demonstrates a complete I2C workflow with CosmicSDK C API
 * and an EEPROM-like target at address 0x50.
 *
 * Workflow:
 * 1. Connect to the adapter and print host device information.
 * 2. Set I2C VTARG to 3.3 V, initialize I2C at 200 kHz, and set pull-ups.
 * 3. Perform a non-stop write/read transaction and verify payload.
 * 4. Perform a standard write/read transaction and verify payload.
 * 5. Scan the I2C bus and print discovered 7-bit and 10-bit addresses.
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.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 print_buffer(const char* label, const uint8_t* buffer, uint32_t length) {
  printf("%s [", label);
  for (uint32_t i = 0; i < length; ++i) {
    printf("%u", buffer[i]);
    if (i + 1u < length) {
      printf(" ");
    }
  }
  printf("]\n");
}

int main(void) {
  int exit_code = 0;
  bool connected = false;
  uint16_t result_code = SUCCESS;
  CosmicSDK_Handle* handle_ptr = CosmicSDK_Create(2000);

  if (handle_ptr == NULL) {
    printf("Could not create CosmicSDK handle\n");
    return 1;
  }

  // Step 1: Connect to the adapter and print host device information.
  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("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);

  // Step 2: Set I2C VTARG to 3.3 V, initialize I2C at 200 kHz, and set pull-ups.
  result_code = CosmicSDK_I2cSetBusVoltage(handle_ptr, 3300, false);
  CHECK_RESULT("I2CSetBusVoltage", result_code);

  result_code = CosmicSDK_I2cControllerInit(handle_ptr, I2C_BUS_A, 200000, I2C_PULLUP_330Ohm);
  if (result_code != SUCCESS && result_code != FW_INTERFACE_ALREADY_INITIALIZED) {
    CHECK_RESULT("I2CControllerInit", result_code);
  }

  result_code = CosmicSDK_I2cSetParameters(handle_ptr, I2C_BUS_A, 200000, I2C_PULLUP_330Ohm);
  CHECK_RESULT("I2CSetParameters", result_code);

  result_code = CosmicSDK_I2cSetPullUpResistors(handle_ptr, I2C_BUS_A, I2C_PULLUP_330Ohm);
  CHECK_RESULT("I2CSetPullUpResistors", result_code);

  const uint8_t target_address = 0x50;
  const uint8_t subaddress[2] = {0x01, 0x00};
  const uint32_t transfer_length = 128;
  uint8_t write_payload[128];
  uint8_t read_payload[128];

  // Step 3: Perform a non-stop write/read transaction and verify payload.
  memset(write_payload, 0xAA, sizeof(write_payload));

  result_code = CosmicSDK_I2cWrite(handle_ptr, I2C_BUS_A, target_address, subaddress, 2,
                                   write_payload, transfer_length, true);
  CHECK_RESULT("I2CWrite(non-stop)", result_code);

  result_code = CosmicSDK_I2cRead(handle_ptr, I2C_BUS_A, target_address, subaddress, 2,
                                  transfer_length, read_payload, true);
  CHECK_RESULT("I2CRead(non-stop)", result_code);

  if (memcmp(write_payload, read_payload, transfer_length) != 0) {
    printf("Non-stop write/read verification failed\n");
    exit_code = 1;
    goto cleanup;
  }
  print_buffer("Non-stop payload:", read_payload, transfer_length);

  // Step 4: Perform a standard write/read transaction and verify payload.
  memset(write_payload, 0xFF, sizeof(write_payload));

  result_code = CosmicSDK_I2cWrite(handle_ptr, I2C_BUS_A, target_address, subaddress, 2,
                                   write_payload, transfer_length, false);
  CHECK_RESULT("I2CWrite", result_code);

  result_code = CosmicSDK_I2cRead(handle_ptr, I2C_BUS_A, target_address, subaddress, 2,
                                  transfer_length, read_payload, false);
  CHECK_RESULT("I2CRead", result_code);

  if (memcmp(write_payload, read_payload, transfer_length) != 0) {
    printf("Write/read verification failed\n");
    exit_code = 1;
    goto cleanup;
  }
  print_buffer("Standard payload:", read_payload, transfer_length);

  // Step 5: Scan the I2C bus and print discovered 7-bit and 10-bit addresses.
  uint8_t count7BitAddresses = 0;
  uint8_t count10BitAddresses = 0;
  uint8_t deviceAddresses[64];
  result_code = CosmicSDK_I2cScanBus(handle_ptr, deviceAddresses, (uint8_t)sizeof(deviceAddresses),
                                     &count7BitAddresses, &count10BitAddresses, I2C_BUS_A, true);
  CHECK_RESULT("I2CScanBus", result_code);

  printf("7-bit Address devices:\n");
  for (uint8_t i = 0; i < count7BitAddresses; ++i) {
    printf("\t0x%02X\n", deviceAddresses[i]);
  }

  printf("10-bit Address devices:\n");
  for (uint8_t i = 0; i < count10BitAddresses; ++i) {
    uint16_t address = (uint16_t)(((uint16_t)deviceAddresses[count7BitAddresses + (2u * i)] << 8) |
                                  (uint16_t)deviceAddresses[count7BitAddresses + (2u * i) + 1u]);
    printf("\t0x%04X\n", address);
  }

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