package ru.kirillius.cooler.controller.API;

import com.fazecast.jSerialComm.SerialPort;
import ru.kirillius.cooler.controller.Application;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public final class SerialMessageSystem extends MessageSystem {
    private final SerialPort port;
    private final InputStream inputStream;
    private final OutputStream outputStream;
    private final Thread readingThread;


    public SerialMessageSystem(SerialPort port) throws MessageException {
        super();

        this.port = port;
        if (!port.openPort()) {
            throw new MessageException("Failed to open serial port");
        }


        port.setBaudRate(9600);

        inputStream = port.getInputStream();
        outputStream = port.getOutputStream();

        readingThread = new Thread(() -> {
            int errors = 0;

            while (!Thread.interrupted()) {
                try {
                    if (inputStream.available() >= 4) {
                        Message message = readMessage();
                        if (message.isValid()) {
                            if (Application.DEBUG && message.getActionType() != ActionType.ERROR) {
                                System.out.println("<<< Message received: " + message);
                            }
                            OnMessageReceived(message);
                        } else {
                            System.err.println("Invalid message received: " + message);
                            errors++;
                            if (errors >= 10) {
                                errors = 0;
                                System.err.println("Too many errors. Cleaning stream buffer...");
                                while (inputStream.available() > 0) //noinspection ResultOfMethodCallIgnored
                                    inputStream.read();
                            }
                        }
                    }
                } catch (MessageException | IOException e) {
                    return;
                }

                try {
                    //noinspection BusyWait
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    return;
                }
            }
            alive = false;
        });

        readingThread.start();
    }


    public synchronized void sendMessage(Message message) {
        message.validate();
        if (Application.DEBUG) {
            System.out.println(">>> Message sent: " + message);
        }
        try {
            outputStream.write(message.toBytes());
        } catch (IOException e) {
            System.err.println("Failed to send message: " + message);
            e.printStackTrace(System.err);
        }
    }


    private Message readMessage() throws MessageException {

        byte[] buffer = new byte[4];
        try {
            //noinspection ResultOfMethodCallIgnored
            inputStream.read(buffer, 0, 4);
        } catch (IOException e) {
            throw new MessageException("Failed to read message", e);
        }
        return new Message(buffer);

    }

    @Override
    protected void OnClose() {
        readingThread.interrupt();

        try {
            inputStream.close();
        } catch (IOException ignored) {
        }
        try {
            outputStream.close();
        } catch (IOException ignored) {
        }
        port.closePort();
    }


}
