using CosmicDriver;

var sync = new object();
int rxNotifications = 0;

using var sdk = new CosmicSdk(2000);
if (!sdk.Connect()) throw new Exception("Failed to connect to a Binho device.");

sdk.OnNotification(notification =>
{
    if (notification.Type != NotificationType.NOTIF_UART || notification.UartRx is null)
    {
        return;
    }

    Console.WriteLine("[UART Notification] " + string.Concat(notification.UartRx.Data.Select(b => (char)b)));
    lock (sync)
    {
        rxNotifications += 1;
        Monitor.PulseAll(sync);
    }
});

var (status, info) = sdk.GetDeviceInfo();
AssertStatus(status, "GetDeviceInfo");
PrintDeviceInfo(info!);

AssertStatus(sdk.UartSetBusVoltage(3300), "UartSetBusVoltage");
AssertStatus(sdk.UartInit(UartBaudRate.UART_BAUD_115200, UartHwHandshake.UART_HW_HANDSHAKE_DIS, UartParity.UART_PARITY_NONE, UartDataSize.UART_DATA_SIZE_8, UartStopBit.UART_STOP_BIT_1), "UartInit", ResultCode.SUCCESS, ResultCode.FW_INTERFACE_ALREADY_INITIALIZED);

byte[] message = "Hello, world!"u8.ToArray();
AssertStatus(sdk.UartSendMessage(message), "UartSendMessage(115200)");
WaitForRx(1, "115200 send");

AssertStatus(sdk.UartSetParameters(UartBaudRate.UART_BAUD_9600, UartHwHandshake.UART_HW_HANDSHAKE_DIS, UartParity.UART_PARITY_NONE, UartDataSize.UART_DATA_SIZE_8, UartStopBit.UART_STOP_BIT_1), "UartSetParameters");
AssertStatus(sdk.UartSendMessage(message), "UartSendMessage(9600)");
WaitForRx(2, "9600 send");

void WaitForRx(int expectedCount, string stage)
{
    lock (sync)
    {
        var timeout = DateTime.UtcNow.AddSeconds(2);
        while (rxNotifications < expectedCount && DateTime.UtcNow < timeout)
        {
            Monitor.Wait(sync, TimeSpan.FromMilliseconds(50));
        }
    }
    if (rxNotifications < expectedCount)
    {
        throw new Exception($"Timeout while waiting for UART loopback notification at {stage}.");
    }
}

static void PrintDeviceInfo(DeviceInfo info)
{
    Console.WriteLine($"Manufacturer: {info.Manufacturer}");
    Console.WriteLine($"Product name: {info.ProductName}");
    Console.WriteLine($"Serial number: {info.SerialNumber}");
    Console.WriteLine($"FW version: {info.FwVersion}");
    Console.WriteLine($"HW version: {info.HwVersion}");
}

static void AssertStatus(ResultCode status, string operation, params ResultCode[] expected)
{
    if (expected.Length == 0) expected = [ResultCode.SUCCESS];
    if (!expected.Contains(status)) throw new Exception($"{operation} failed. STATUS=0x{(ushort)status:X4}");
}
