/**
 * @file example.cpp
 * @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 <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <memory>
#include <vector>

#include "CosmicSDK.hpp"

static bool checkResult(uint16_t code, const char* operation) {
  if (code != SUCCESS) {
    std::cerr << operation << " failed. ERROR CODE: " << code << std::endl;
    return false;
  }
  return true;
}

static void printPayload(const char* label, const std::vector<uint8_t>& payload) {
  std::cout << label << " [";
  for (size_t i = 0; i < payload.size(); ++i) {
    std::cout << static_cast<int>(payload[i]);
    if (i + 1u < payload.size()) {
      std::cout << " ";
    }
  }
  std::cout << "]" << std::endl;
}

int main() {
  auto device = std::make_unique<CosmicSDK>(2000);
  if (!device->connect()) {
    std::cerr << "Failed to connect to a Binho device" << std::endl;
    return 1;
  }

  // Step 1: Connect to the adapter and print host device information.
  CosmicSDK::DeviceInfo deviceInformation;
  if (!checkResult(device->getDeviceInfo(deviceInformation), "getDeviceInfo")) {
    device->disconnect();
    return 1;
  }

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

  // Step 2: Set I2C VTARG to 3.3 V, initialize I2C at 200 kHz, and set pull-ups.
  if (!checkResult(device->i2cSetBusVoltage(3300, false), "i2cSetBusVoltage")) {
    device->disconnect();
    return 1;
  }

  const uint16_t init_status = device->i2cControllerInit(I2C_BUS_A, 200000, I2C_PULLUP_330Ohm);
  if (init_status != SUCCESS && init_status != FW_INTERFACE_ALREADY_INITIALIZED) {
    std::cerr << "i2cControllerInit failed. ERROR CODE: " << init_status << std::endl;
    device->disconnect();
    return 1;
  }

  if (!checkResult(device->i2cSetParameters(I2C_BUS_A, 200000, I2C_PULLUP_330Ohm), "i2cSetParameters")) {
    device->disconnect();
    return 1;
  }

  if (!checkResult(device->i2cSetPullUpResistors(I2C_BUS_A, I2C_PULLUP_330Ohm), "i2cSetPullUpResistors")) {
    device->disconnect();
    return 1;
  }

  const uint8_t target_address = 0x50;
  const std::vector<uint8_t> subaddress = {0x01, 0x00};
  const uint16_t transferLength = 128;
  std::vector<uint8_t> writePayload(transferLength, 0xAA);
  std::vector<uint8_t> readPayload;

  // Step 3: Perform a non-stop write/read transaction and verify payload.
  if (!checkResult(device->i2cWrite(I2C_BUS_A, target_address, subaddress, writePayload, true),
                   "i2cWrite(non-stop)")) {
    device->disconnect();
    return 1;
  }

  if (!checkResult(device->i2cRead(I2C_BUS_A, target_address, subaddress, transferLength, readPayload, true),
                   "i2cRead(non-stop)")) {
    device->disconnect();
    return 1;
  }

  if (readPayload != writePayload) {
    std::cerr << "Non-stop write/read verification failed" << std::endl;
    device->disconnect();
    return 1;
  }
  printPayload("Non-stop payload:", readPayload);

  // Step 4: Perform a standard write/read transaction and verify payload.
  std::fill(writePayload.begin(), writePayload.end(), 0xFF);

  if (!checkResult(device->i2cWrite(I2C_BUS_A, target_address, subaddress, writePayload, false),
                   "i2cWrite")) {
    device->disconnect();
    return 1;
  }

  if (!checkResult(device->i2cRead(I2C_BUS_A, target_address, subaddress, transferLength, readPayload, false),
                   "i2cRead")) {
    device->disconnect();
    return 1;
  }

  if (readPayload != writePayload) {
    std::cerr << "Write/read verification failed" << std::endl;
    device->disconnect();
    return 1;
  }
  printPayload("Standard payload:", readPayload);

  // Step 5: Scan the I2C bus and print discovered 7-bit and 10-bit addresses.
  uint8_t count7BitAddresses = 0;
  uint8_t count10BitAddresses = 0;
  std::vector<uint8_t> deviceAddresses;
  if (!checkResult(device->i2cScanBus(deviceAddresses, &count7BitAddresses, &count10BitAddresses,
                                      I2C_BUS_A, true),
                   "i2cScanBus")) {
    device->disconnect();
    return 1;
  }

  std::cout << "7-bit Address devices:" << std::endl;
  for (uint8_t i = 0; i < count7BitAddresses; ++i) {
    std::cout << "\t0x" << std::hex << std::setw(2) << std::setfill('0')
              << static_cast<int>(deviceAddresses[i]) << std::dec << std::endl;
  }

  std::cout << "10-bit Address devices:" << std::endl;
  for (uint8_t i = 0; i < count10BitAddresses; ++i) {
    const uint16_t address =
        static_cast<uint16_t>((static_cast<uint16_t>(deviceAddresses[count7BitAddresses + (2u * i)]) << 8) |
                              static_cast<uint16_t>(deviceAddresses[count7BitAddresses + (2u * i) + 1u]));
    std::cout << "\t0x" << std::hex << std::setw(4) << std::setfill('0')
              << address << std::dec << std::endl;
  }

  device->disconnect();
  return 0;
}
