package com.binho.cosmicsdk.examples.uart;

import com.binho.cosmicsdk.CosmicSdk;
import com.binho.cosmicsdk.ResultCodes;
import com.binho.cosmicsdk.Types.NotificationType;
import com.binho.cosmicsdk.Types.UartBaudRate;
import com.binho.cosmicsdk.Types.UartDataSize;
import com.binho.cosmicsdk.Types.UartHwHandshake;
import com.binho.cosmicsdk.Types.UartParity;
import com.binho.cosmicsdk.Types.UartStopBit;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.binho.cosmicsdk.examples.ExampleHelpers.assertStatus;
import static com.binho.cosmicsdk.examples.ExampleHelpers.printDeviceInfo;

public final class UartExample {
    private UartExample() {
    }

    public static void main(String[] args) throws Exception {
        AtomicInteger rxCount = new AtomicInteger(0);

        try (CosmicSdk sdk = new CosmicSdk(2000)) {
            sdk.onNotification(notification -> {
                if (notification.type() != NotificationType.NOTIF_UART || notification.uartRx() == null) {
                    return;
                }
                System.out.println("[UART Notification] " + new String(notification.uartRx().data(), StandardCharsets.UTF_8));
                rxCount.incrementAndGet();
            });

            if (!sdk.connect()) {
                throw new IllegalStateException("Failed to connect to a Binho device.");
            }

            var infoResult = sdk.getDeviceInfo();
            assertStatus(infoResult.status(), "getDeviceInfo");
            printDeviceInfo(infoResult.value());

            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",
                    ResultCodes.SUCCESS,
                    ResultCodes.FW_INTERFACE_ALREADY_INITIALIZED
            );

            byte[] message = "Hello, world!".getBytes(StandardCharsets.UTF_8);
            assertStatus(sdk.uartSendMessage(message), "uartSendMessage(115200)");
            waitForCount(rxCount, 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)");
            waitForCount(rxCount, 2, "9600 send");
        }
    }

    private static void waitForCount(AtomicInteger count, int expected, String stage) throws InterruptedException {
        long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2);
        while (System.nanoTime() < deadline) {
            if (count.get() >= expected) {
                return;
            }
            Thread.sleep(20);
        }
        throw new IllegalStateException("Timeout while waiting for UART loopback notification at " + stage + ".");
    }
}
