/**
 * @file example.cpp
 * @brief I3C HDR-DDR Example with Binho Supernova (Blocking C++ API)
 *
 * This example demonstrates I3C HDR-DDR transfers using:
 * - Binho Supernova
 * - Binho mikroBUS Adapter Board
 * - MikroE 6DOF IMU 14 Click
 *
 * Workflow:
 * 1. Connect to the adapter and print host device information.
 * 2. Set I3C VTARG to 3.3 V, initialize I3C controller, and initialize I3C bus.
 * 3. Execute one HDR-DDR write transaction.
 * 4. Reset the target internal register pointer with an SDR write.
 * 5. Execute one HDR-DDR read transaction and print the received bytes.
 */

#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;
}

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

  // Step 1: Create SDK instance and connect to the adapter.
  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;

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

  // Step 1.1: Read and print host adapter device information.
  CosmicSDK::DeviceInfo deviceInformation;
  if (device->getDeviceInfo(deviceInformation) != SUCCESS) {
    std::cerr << "Failed to read device information!" << std::endl;
    device->disconnect();
    return 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;

  // Step 2: Set VTARG, initialize I3C controller, and initialize I3C bus.
  std::cout << "Setting voltage to 3.3v: ";
  uint16_t result_code = device->i3cSetBusVoltage(3300);
  if (!checkResult(result_code, "i3cSetBusVoltage")) {
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;

  std::cout << "Initializing I3C controller: ";
  result_code = device->i3cControllerInit(I3cPushPullRate::PUSH_PULL_3_125_MHz_DC_31_25, I3cOpenDrainTransferRate::OPEN_DRAIN_1_MHz, I2cTransferRate::FAST_MODE, I3cDriveStrength::STANDARD);
  if (result_code != SUCCESS && result_code != FW_INTERFACE_ALREADY_INITIALIZED) {
    std::cerr << "Failed to init the I3C peripheral! ERROR CODE: " << result_code << std::endl;
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;
  
  std::cout << "Initializing I3C bus: ";
  result_code = device->i3cInitBus();
  if (!checkResult(result_code, "i3cInitBus")) {
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;
  
  // Step 3: Execute one HDR-DDR write transaction.
  std::cout << "Hdr-Ddr write: ";
  static std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  result_code = device->i3cHdrDdrWrite(0x08, 0x04, data);
  if (!checkResult(result_code, "i3cHdrDdrWrite")) {
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;
  
  // Step 4: Reset target internal register pointer with an SDR write.
  std::cout << "Reset the internal register address of the ICM426xx target to 0x00: ";
  data = {0x00};
  result_code = device->i3cSdrWrite(0x08, data, {}, I3cTransferFlags::I3C_XFER_START_WITH_7E);
  if (!checkResult(result_code, "i3cSdrWrite")) {
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;
  
  // Step 5: Execute one HDR-DDR read transaction and print returned bytes.
  std::cout << "Hdr-Ddr read: ";
  static std::vector<uint8_t> dataOut;
  result_code = device->i3cHdrDdrRead(0x08, 0x80, 10, dataOut);
  if (!checkResult(result_code, "i3cHdrDdrRead")) {
    device->disconnect();
    return 1;
  }
  std::cout << "OK" << std::endl;
  std::cout << "\tDATA: ";
  for (uint8_t i = 0; i < dataOut.size(); ++i) {
      std::cout << (int)dataOut[i] << " ";
  }
  std::cout << std::dec << std::endl;

  // Cleanup: Disconnect device.
  device->disconnect();

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