package ru.kirillius.cooler.controller.CLI;

import ru.kirillius.cooler.controller.API.*;
import ru.kirillius.cooler.controller.Application;
import ru.kirillius.cooler.controller.ExtMath;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public final class ConsoleApp {
    public final static BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    private final Menu<Boolean> mainMenu;
    private volatile MessageSystem messageSystem = null;

    public ConsoleApp() {
        try {
            selectPort(true);

        } catch (MessageException e) {
            e.printStackTrace();
            System.exit(1);
        }

        mainMenu = new Menu<>("Main menu") {
            @Override
            protected Boolean OnSelect(int index, MenuItem<Boolean> item, Boolean value) {
                if (!value) close();
                return value;
            }
        };
        reload();
        //noinspection StatementWithEmptyBody
        while (Boolean.TRUE.equals(mainMenu.show())) ;
        System.exit(0);
    }

    private void showError(Throwable t) {
        t.printStackTrace(System.err);
        System.exit(1);
    }

    private void reload() {
        mainMenu.removeAll();
        mainMenu.add(new CallbackMenuItem<>("Change port") {
            @Override
            public Boolean select() {
                try {
                    selectPort(false);
                    reload();
                } catch (MessageException e) {
                    showError(e);
                }
                return true;
            }
        });
        mainMenu.add(new CallbackMenuItem<>("Read stats") {
            @Override
            public Boolean select() {
                Operation tempOp = messageSystem.getValue(DataType.TEMP);
                tempOp.waitForReturn();
                if (!tempOp.isSuccessful()) {
                    System.err.println("Failed to read temp");
                    return true;
                }

                Operation levelOp = messageSystem.getValue(DataType.LEVEL);
                levelOp.waitForReturn();
                if (!levelOp.isSuccessful()) {
                    System.err.println("Failed to read power level");
                    return true;
                }

                System.out.println("Current temp = " + Byte.toUnsignedInt(tempOp.getValue()) + " °C");
                System.out.println("Current power = " + (int) (ExtMath.inverseLerp(0, 255, Byte.toUnsignedInt(tempOp.getValue())) * 100) + "%");


                return true;
            }
        });
        mainMenu.add(new SubmenuItem<Boolean, SettingsMenu, Void>(new SettingsMenu(messageSystem)) {
            @Override
            protected Boolean OnSelected(Void value) {
                return true;
            }
        });
        mainMenu.add(new CallbackMenuItem<>("Reboot") {
            @Override
            public Boolean select() {
                Operation operation = messageSystem.sendCommand(ActionType.REBOOT);
                operation.waitForReturn();
                System.out.println(operation.isSuccessful() ? "Reboot OK" : "Reboot failed!");
                try {
                    Thread.sleep(10000L);
                } catch (InterruptedException e) {
                    return false;
                }
                return true;
            }
        });
        mainMenu.add(new CallbackMenuItem<>("Exit") {
            @Override
            public Boolean select() {
                return false;
            }
        });


    }


    private void selectPort(boolean useDefaults) throws MessageException {
        if (messageSystem != null) messageSystem.close();
        var menu = new PortSelectionMenu();
        var port = useDefaults ? menu.selectByName(Application.properties.getProperty("port")) : menu.show();
        if (port != null) {
            messageSystem = new SerialMessageSystem(port);
            Application.properties.setProperty("port", port.getSystemPortName());
            try {
                Application.saveConfig();
            } catch (IOException e) {
                showError(e);
            }
        } else {
            messageSystem = new DummyMessageSystem();
        }


    }
}
