package com.testserial;

import com.fazecast.jSerialComm.SerialPort;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.*;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SerialController {

    @FXML private ComboBox<String> portCombo;
    @FXML private ComboBox<Integer> baudCombo;
    @FXML private Button connectBtn, sendBtn1, sendBtn2;
    @FXML private TextArea outputArea;

    private SerialPort activePort;
    private OutputStream out;
    private volatile boolean keepReading = false;

    @FXML
    public void initialize() {

        baudCombo.getItems().addAll(9600, 19200, 38400, 57600, 115200, 500000);
        baudCombo.getSelectionModel().select(4); // default 115200

        refreshPorts();

        connectBtn.setOnAction(e -> connectPort());
        sendBtn1.setOnAction(e -> sendData("Button 1\r\n"));
        sendBtn2.setOnAction(e -> sendData("Button 2\r\n"));
    }

    private void refreshPorts() {
        if (activePort != null && activePort.isOpen()) {
            return; // ❗ DO NOT refresh while connected
        }

        List<String> ports = Arrays.stream(SerialPort.getCommPorts())
                .map(SerialPort::getSystemPortName)
                .collect(Collectors.toList());

        portCombo.getItems().setAll(ports);

        if (!ports.isEmpty() && portCombo.getValue() == null) {
            portCombo.getSelectionModel().selectFirst();
        }
    }

    private void connectPort() {

        if (activePort != null && activePort.isOpen()) {
            keepReading = false;
            activePort.closePort();
            connectBtn.setText("Connect");
            outputArea.appendText("Disconnected\n");
            return;
        }

        String portName = portCombo.getValue();
        Integer baudRate = baudCombo.getValue();

        if (portName == null || baudRate == null) {
            outputArea.appendText("Select port & baud rate\n");
            return;
        }

        activePort = SerialPort.getCommPort(portName);
        activePort.setBaudRate(baudRate);

        // ✅ Correct timeout mode
        activePort.setComPortTimeouts(
        	    SerialPort.TIMEOUT_READ_SEMI_BLOCKING,
        	    200,   // short timeout, like Arduino Monitor
        	    0
        	);

        if (!activePort.openPort()) {
            outputArea.appendText("Failed to open port\n");
            return;
        }

        out = activePort.getOutputStream();
        keepReading = true;

        connectBtn.setText("Disconnect");
        outputArea.appendText("Connected to " + portName + "\n");

        startReadThread();
    }

    private void startReadThread() {
        Thread readThread = new Thread(() -> {
            try (InputStream in = activePort.getInputStream()) {
                byte[] buffer = new byte[256];

                while (keepReading && activePort.isOpen()) {
                    int len = in.read(buffer);   // waits quietly

                    if (len > 0) {
                        String data = new String(buffer, 0, len);
                        Platform.runLater(() -> outputArea.appendText(data));
                    }
                    // len == 0 → just means "no data yet" (NORMAL)
                }
            } catch (Exception e) {
                // 🔕 EXACT Arduino Serial Monitor behavior:
                // Ignore timeout exceptions
                String msg = e.getMessage().toLowerCase();
                if (!msg.contains("timed out")) {
                    Platform.runLater(() ->
                        outputArea.appendText("Read error: " + e.getMessage() + "\n")
                    );
                }
            }
        });

        readThread.setDaemon(true);
        readThread.start();
    }


    private void sendData(String data) {
        try {
            if (activePort != null && activePort.isOpen()) {
                out.write(data.getBytes(StandardCharsets.UTF_8));
                out.flush();
                outputArea.appendText("Sent: " + data);
            } else {
                outputArea.appendText("Not connected\n");
            }
        } catch (Exception e) {
            outputArea.appendText("Send error\n");
        }
    }
}
