/**
 * @file example.cpp
 * @brief UART Loopback Example with Binho Supernova/Pulsar (Blocking C++ API)
 *
 * This example demonstrates a UART loopback workflow using CosmicSDK C++ API
 * and a setup composed of:
 * - Binho Supernova or Pulsar
 * - Binho Breakout Board
 *
 * Required wiring:
 * - Interconnect TX and RX on the breakout board to close the hardware loop.
 *
 * Workflow:
 * 1. Create SDK instance and register a UART notification callback.
 * 2. Connect to the adapter and read device information.
 * 3. Initialize UART at 115200-8N1 and send a message.
 * 4. Receive data through the registered notification callback.
 * 5. Reconfigure UART to 9600-8N1, send again, and observe callback reception.
 */

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <vector>

#include "CosmicSDK.hpp"

int main() {
  std::mutex rx_mutex;
  std::condition_variable rx_cv;
  uint32_t rx_notifications = 0;

  auto wait_for_rx = [&](uint32_t expected_count, const char* stage) -> bool {
    std::unique_lock<std::mutex> lock(rx_mutex);
    const bool received = rx_cv.wait_for(lock, std::chrono::seconds(2), [&]() {
      return rx_notifications >= expected_count;
    });
    if (!received) {
      std::cerr << "Timeout while waiting for UART loopback notification at " << stage << std::endl;
      return false;
    }
    return true;
  };

  // Step 1: Create SDK instance and register UART notification callback.
  auto device = std::make_unique<CosmicSDK>(2000);
  device->onNotification([&](const Notification& notification) {
    if (const UartRxNotification* payload = notification.uartRx()) {
      std::cout << "[UART Notification] ";
      for (uint8_t i = 0; i < payload->dataLength; ++i) {
        std::cout << payload->data[i];
      }
      std::cout << std::endl;
      {
        std::lock_guard<std::mutex> lock(rx_mutex);
        ++rx_notifications;
      }
      rx_cv.notify_one();
    }
  });

  // Step 2: Connect and read adapter information.
  if (!device->connect()) {
    std::cerr << "Failed to connect to a Binho Device!" << std::endl;
    return 1;
  }

  CosmicSDK::DeviceInfo deviceInformation;
  if (device->getDeviceInfo(deviceInformation) != SUCCESS) {
    std::cerr << "Failed to read device information!" << std::endl;
    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 3: Initialize UART at 115200-8N1 and send loopback message.
  uint16_t init_status = device->uartInit(UartBaudRate::UART_BAUD_115200, UartHwHandshake::UART_HW_HANDSHAKE_DIS,
                                          UartParity::UART_PARITY_NONE, UartDataSize::UART_DATA_SIZE_8,
                                          UartStopBit::UART_STOP_BIT_1);
  if (init_status != SUCCESS && init_status != FW_INTERFACE_ALREADY_INITIALIZED) {
    std::cerr << "Failed to initialize the UART! ERROR CODE:" << init_status << std::endl;
    device->disconnect();
    return 1;
  }

  const std::string message = "Hello, world!";
  std::vector<uint8_t> formattedMessage(message.begin(), message.end());
  init_status = device->uartSendMessage(formattedMessage);
  if (init_status != SUCCESS) {
    std::cerr << "Failed to send UART message at 115200! ERROR CODE:" << init_status << std::endl;
    device->disconnect();
    return 1;
  }

  // Step 4: Receive data through notification callback.
  if (!wait_for_rx(1, "115200 send")) {
    device->disconnect();
    return 1;
  }

  // Step 5: Reconfigure UART to 9600-8N1, send again, observe callback reception.
  init_status = device->uartSetParameters(UartBaudRate::UART_BAUD_9600, UartHwHandshake::UART_HW_HANDSHAKE_DIS,
                                          UartParity::UART_PARITY_NONE, UartDataSize::UART_DATA_SIZE_8,
                                          UartStopBit::UART_STOP_BIT_1);
  if (init_status != SUCCESS) {
    std::cerr << "Failed to set the UART parameters! ERROR CODE:" << init_status << std::endl;
    device->disconnect();
    return 1;
  }

  init_status = device->uartSendMessage(formattedMessage);
  if (init_status != SUCCESS) {
    std::cerr << "Failed to send UART message at 9600! ERROR CODE:" << init_status << std::endl;
    device->disconnect();
    return 1;
  }

  if (!wait_for_rx(2, "9600 send")) {
    device->disconnect();
    return 1;
  }
  device->disconnect();
  return 0;
}
