#include "UI_Graphics.h"

const char *C_UI_Graphics::PowerSupplies_ShortNames[] = {"12V", "Lap", "QC", "PD"};
const char *C_UI_Graphics::PowerSupplies_MenuNames[] = {"< 12V Output >", "<   Laptop   >", "<   USB QC   >", "<   USB PD   >"};

const char C_UI_Graphics::labelFuseLowered1[] PROGMEM = " |-> Iout limited to 1.5A due";
const char C_UI_Graphics::labelFuseLowered2[] PROGMEM = "    to high Lap current draw.";

const char C_UI_Graphics::STR_DEVICE_NAME[] PROGMEM = "CAR CHARGER";
const char C_UI_Graphics::STR_VERSION_YEAR[] PROGMEM = "v1.0, 2021";
const char C_UI_Graphics::STR_SYSTEMP_LINE[] PROGMEM = "Sys temp:  %.2f °C";
const char C_UI_Graphics::STR_FANSPEED_LINE[] PROGMEM = "FAN Speed:  %d RPM";
const char C_UI_Graphics::STR_FANPWM_LINE[] PROGMEM = "FAN PWM:  %d %%";
const char C_UI_Graphics::STR_TOTAL_ARROW[] PROGMEM = "     TOTAL    >";
const char C_UI_Graphics::STR_POUT[] PROGMEM = "Pout = %.1f W";
const char C_UI_Graphics::STR_ACTIVE[] PROGMEM = "Active:  ";
const char C_UI_Graphics::STR_UOUT[] PROGMEM = "Uout = %.1f V";
const char C_UI_Graphics::STR_IOUT[] PROGMEM = "Iout = %.1f A";
const char C_UI_Graphics::STR_SELECTED_V[] PROGMEM = "Selected: %s (%.1f V)";
const char C_UI_Graphics::STR_ABOUT[] PROGMEM = "<    ABOUT     ";
const char C_UI_Graphics::STR_ABOUT_YEAR_VERSION[] PROGMEM = "HW v1.0, FW v1.0 (2021)";
const char C_UI_Graphics::STR_SELECT_HEADING[] PROGMEM = "SELECT";
const char C_UI_Graphics::STR_LAPTOP_BRAND[] PROGMEM = "Laptop brand:";
const char C_UI_Graphics::STR_LAPTOP_SELECTION[] PROGMEM = "%c %s %c";
const char C_UI_Graphics::STR_CONFIRM[] PROGMEM = "Press to confirm.";
const char C_UI_Graphics::STR_SET_VOLTAGE_HEADING[] PROGMEM = "SET VOLTAGE";
const char C_UI_Graphics::STR_SET_VOLTAGE[] PROGMEM = "Please set voltage";
const char C_UI_Graphics::STR_SELECTED_LAPTOP_V[] PROGMEM = "%c %.1f V %c";
const char C_UI_Graphics::STR_FUSE_TRIGGERED_HEADING[] PROGMEM = "FUSE TRIGGERED";
const char C_UI_Graphics::STR_CURRENT_LIMIT1[] PROGMEM = "Current limit reached";
const char C_UI_Graphics::STR_CURRENT_LIMIT2[] PROGMEM = "on output: %s!";
const char C_UI_Graphics::STR_CURRENT_LIMIT3[] PROGMEM = "Output suspended.";


const uint8_t C_UI_Graphics::logo_bits[] U8X8_PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x02, 0xA8, 0xA1, 0x00, 0x02, 0x00, 
  0x00, 0xF0, 0x07, 0xFE, 0xF9, 0x00, 0x1F, 0x00, 0x00, 0xF8, 0x07, 0xFF, 
  0x7C, 0x80, 0x0F, 0x00, 0x00, 0xF8, 0x87, 0x7F, 0x7C, 0xC0, 0x0F, 0x00, 
  0x00, 0xFC, 0xC7, 0x7F, 0x3E, 0xC0, 0x07, 0x00, 0x00, 0xFC, 0xE7, 0x3F, 
  0x3E, 0xE0, 0x03, 0x00, 0x00, 0xFE, 0xFF, 0x3F, 0x1F, 0xE0, 0x03, 0x00, 
  0x00, 0xDE, 0xFF, 0x9F, 0x1F, 0xF0, 0x01, 0x00, 0x00, 0xDF, 0x7F, 0x8F, 
  0x0F, 0xF0, 0x01, 0x00, 0x00, 0x9F, 0xBF, 0xCF, 0x07, 0xF8, 0x00, 0x00, 
  0x80, 0x8F, 0xDF, 0xCF, 0x07, 0xF8, 0x00, 0x00, 0xC0, 0x87, 0xC7, 0xE7, 
  0x03, 0x7C, 0x00, 0x00, 0xC0, 0x07, 0xE0, 0xE3, 0x03, 0x7E, 0x00, 0x00, 
  0xE0, 0x03, 0xE0, 0xF3, 0x03, 0x3F, 0x00, 0x00, 0xE0, 0x03, 0xF0, 0xF1, 
  0xFF, 0x1F, 0x00, 0x00, 0xF0, 0x01, 0xF0, 0xF9, 0xFF, 0x1F, 0x00, 0x00, 
  0xF8, 0x00, 0xF8, 0xF8, 0xFF, 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x7C, 0xF8, 
  0xFF, 0x03, 0x00, 0x00, 0x38, 0x00, 0x6C, 0x70, 0x7F, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0xC0, 0x01, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x43, 0xD8, 0x00, 0x00, 
  0x80, 0x7F, 0x00, 0x00, 0xF1, 0xEC, 0x02, 0x00, 0xE0, 0xFF, 0x00, 0x80, 
  0x98, 0x55, 0x02, 0x00, 0xF8, 0x83, 0x00, 0xC6, 0xC8, 0xDC, 0x01, 0x00, 
  0x38, 0x81, 0x39, 0xC7, 0xEC, 0xEE, 0x00, 0x00, 0x38, 0x81, 0x2D, 0x67, 
  0xB6, 0x4D, 0x00, 0x00, 0x9C, 0x81, 0xB6, 0x64, 0xDE, 0x00, 0x00, 0x00, 
  0x80, 0x80, 0xCE, 0xF4, 0x49, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC6, 0xCB, 
  0x60, 0x00, 0x00, 0x00, 0x40, 0x60, 0xA6, 0x07, 0x30, 0x00, 0x00, 0x00, 
  0x60, 0x38, 0x1E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x1E, 0x0C, 0x00, 
  0x18, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };

void C_UI_Graphics::Init() {
    u8g2.begin();
}

void C_UI_Graphics::DrawWelcomeScreen() {
    u8g2.firstPage();
    do {
        u8g2.setFont(FONT_WELCOME_HEADING);

        int y = u8g2.getMaxCharHeight();
        sprintf_P(buff, STR_DEVICE_NAME);
        int strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

        y += 2;
        u8g2.drawXBMP(u8g2.getDisplayWidth() / 2 - logo_width / 2, y, logo_width, logo_height, logo_bits);

        u8g2.setFont(FONT_WELCOME_TEXT);

        strW = u8g2.getStrWidth(STR_VERSION_YEAR);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, u8g2.getDisplayHeight() - 2, STR_VERSION_YEAR);
    } while (u8g2.nextPage());
}

void C_UI_Graphics::DrawMenu_Totals() {
    u8g2.setFont(FONT_NORMAL_HEADING);

    sprintf_P(buff, STR_TOTAL_ARROW);
    int strW = u8g2.getStrWidth(buff);
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, u8g2.getMaxCharHeight(), buff);
    int y = u8g2.getMaxCharHeight() + 2;
    u8g2.drawHLine(0, y, u8g2.getDisplayWidth());

    u8g2.setFont(FONT_NORMAL_TEXT);

    float totalPower = Power_12V.GetPower() + Power_Laptop.GetPower() + Power_USB_PD.GetPower() + Power_USB_QC.GetPower();
    sprintf_P(buff, STR_POUT, totalPower);
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_ACTIVE);
    bool drawComma = false;
    for (int i = 0; i < N_SUPPLIES; i++) {
        if (PowerSupplies[i]->IsEnabled()) {
            if (drawComma) strcat(buff, "  |  ");
            strcat(buff, PowerSupplies_ShortNames[i]);
            drawComma = true;
        }
    }
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_SYSTEMP_LINE, CoolingSystem.GetTemperature());
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_FANSPEED_LINE, CoolingSystem.GetFanRPM());
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_FANPWM_LINE, (100 * CoolingSystem.GetFanPWM()) / 255);
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);
}

void C_UI_Graphics::DrawMenu_Normal(int supplyId) {
    u8g2.setFont(FONT_NORMAL_HEADING);

    int strW = u8g2.getStrWidth(PowerSupplies_MenuNames[supplyId]);
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, u8g2.getMaxCharHeight(), PowerSupplies_MenuNames[supplyId]);
    int y = u8g2.getMaxCharHeight() + 2;
    u8g2.drawHLine(0, y, u8g2.getDisplayWidth());

    u8g2.setFont(FONT_NORMAL_TEXT);

    sprintf_P(buff, STR_UOUT, PowerSupplies[supplyId]->GetVoltage());
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_IOUT, PowerSupplies[supplyId]->GetCurrent());
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_POUT, PowerSupplies[supplyId]->GetPower());
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    if (supplyId == 1) {
        sprintf_P(buff, STR_SELECTED_V, laptopNamesDB[selectedLaptopID], selectedLaptopVoltage);
        y += u8g2.getMaxCharHeight() + 4;
        u8g2.drawStr(0, y, buff);
    } else if (supplyId == 0) {
        if (Power_12V.GetFuseValue() == C_PwrSupply_12V::FUSE_CURRENT_12V_WITH_LAPTOP_5A) {
            y += u8g2.getMaxCharHeight() + 2;
            sprintf_P(buff, labelFuseLowered1);
            u8g2.drawStr(0, y, buff);
            y += u8g2.getMaxCharHeight() + 2;
            sprintf_P(buff, labelFuseLowered2);
            u8g2.drawStr(0, y, buff);
        }
    }
}

void C_UI_Graphics::DrawMenu_About() {
    u8g2.setFont(FONT_NORMAL_HEADING);

    sprintf_P(buff, STR_ABOUT);
    int strW = u8g2.getStrWidth(buff);
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, u8g2.getMaxCharHeight(), buff);
    int y = u8g2.getMaxCharHeight() + 2;
    u8g2.drawHLine(0, y, u8g2.getDisplayWidth());

    u8g2.setFont(FONT_NORMAL_TEXT);

    sprintf_P(buff, STR_ABOUT_YEAR_VERSION);
    strW = u8g2.getStrWidth(buff);
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

    y += 2;
    u8g2.drawXBMP(u8g2.getDisplayWidth() / 2 - logo_width / 2, y, logo_width, logo_height, logo_bits);
}

void C_UI_Graphics::DrawMenu_Laptop() {
    u8g2.setFont(FONT_NORMAL_HEADING);

    sprintf_P(buff, STR_SELECT_HEADING);
    int strW = u8g2.getStrWidth(buff);
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, u8g2.getMaxCharHeight(), buff);
    int y = u8g2.getMaxCharHeight() + 2;
    u8g2.drawHLine(0, y, u8g2.getDisplayWidth());

    u8g2.setFont(FONT_NORMAL_TEXT);

    y += u8g2.getMaxCharHeight() + 2;
    sprintf_P(buff, STR_LAPTOP_BRAND);
    u8g2.drawStr(0, y, buff);

    sprintf_P(buff, STR_LAPTOP_SELECTION, selectedLaptopID == 0 ? ' ' : '<', laptopNamesDB[selectedLaptopID], selectedLaptopID == (LAPTOP_DB_LEN - 1) ? ' ' : '>');
    strW = u8g2.getStrWidth(buff);
    y += u8g2.getMaxCharHeight() + 4;
    u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

    sprintf_P(buff, STR_UOUT, selectedLaptopVoltage);
    y += u8g2.getMaxCharHeight() + 2;
    u8g2.drawStr(0, y, buff);

    y += u8g2.getMaxCharHeight() + 4;
    sprintf_P(buff, STR_CONFIRM);
    u8g2.drawStr(0, y, buff);
}

void C_UI_Graphics::DrawMenu_LaptopVotageNotification() {
    static const int XY_DIST = 5;

    static const int FINAL_SIZE_X = u8g2.getDisplayWidth() - 2 * XY_DIST;
    static const int FINAL_SIZE_Y = u8g2.getDisplayHeight() - 2 * XY_DIST;

    static const int STEP_X = FINAL_SIZE_X / NUM_STEPS;
    static const int STEP_Y = FINAL_SIZE_Y / NUM_STEPS;

    notificationRendered = true;

    if (notificationStep < NUM_STEPS) {
        u8g2.setDrawColor(0x1);  //white
        u8g2.drawBox(u8g2.getDisplayWidth() / 2 - (notificationStep * STEP_X / 2), u8g2.getDisplayHeight() / 2 - (notificationStep * STEP_Y / 2), notificationStep * STEP_X, notificationStep * STEP_Y);
        u8g2.setDrawColor(0x0);
        u8g2.drawFrame(u8g2.getDisplayWidth() / 2 - (notificationStep * STEP_X / 2), u8g2.getDisplayHeight() / 2 - (notificationStep * STEP_Y / 2), notificationStep * STEP_X, notificationStep * STEP_Y);
        u8g2.setDrawColor(0x1);
    } else {
        u8g2.setDrawColor(0x0);  //white
        u8g2.drawBox(XY_DIST, XY_DIST, u8g2.getDisplayWidth() - 2 * XY_DIST, u8g2.getDisplayHeight() - 2 * XY_DIST);
        u8g2.setDrawColor(0x1);
        u8g2.drawFrame(XY_DIST, XY_DIST, u8g2.getDisplayWidth() - 2 * XY_DIST, u8g2.getDisplayHeight() - 2 * XY_DIST);

        u8g2.setFont(FONT_NORMAL_HEADING);

        sprintf_P(buff, STR_SET_VOLTAGE_HEADING);
        int strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, XY_DIST + u8g2.getMaxCharHeight(), buff);
        int y = u8g2.getMaxCharHeight() + 2 + XY_DIST;
        u8g2.drawHLine(XY_DIST, y, u8g2.getDisplayWidth() - 2 * XY_DIST);

        u8g2.setFont(FONT_NORMAL_TEXT);

        y += u8g2.getMaxCharHeight() + 2;
        sprintf_P(buff, STR_SET_VOLTAGE);
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

        y += u8g2.getMaxCharHeight() + 4;
        sprintf_P(buff, STR_SELECTED_LAPTOP_V, selectedLaptopVoltage <= C_PwrSupply_Laptop::LAPTOP_MIN_V ? ' ' : '<', selectedLaptopVoltage, selectedLaptopVoltage >= C_PwrSupply_Laptop::LAPTOP_MAX_V ? ' ' : '>');
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

        y += u8g2.getMaxCharHeight() + 4;
        sprintf_P(buff, STR_CONFIRM);
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);
    }
}

void C_UI_Graphics::DrawMenu_FuseTriggeredNotification(const char *out_name) {
    static const int XY_DIST = 5;

    static const int FINAL_SIZE_X = u8g2.getDisplayWidth() - 2 * XY_DIST;
    static const int FINAL_SIZE_Y = u8g2.getDisplayHeight() - 2 * XY_DIST;

    static const int STEP_X = FINAL_SIZE_X / NUM_STEPS;
    static const int STEP_Y = FINAL_SIZE_Y / NUM_STEPS;

    notificationRendered = true;

    if (notificationStep < NUM_STEPS) {
        u8g2.setDrawColor(0x1);  //white
        u8g2.drawBox(u8g2.getDisplayWidth() / 2 - (notificationStep * STEP_X / 2), u8g2.getDisplayHeight() / 2 - (notificationStep * STEP_Y / 2), notificationStep * STEP_X, notificationStep * STEP_Y);
        u8g2.setDrawColor(0x0);
        u8g2.drawFrame(u8g2.getDisplayWidth() / 2 - (notificationStep * STEP_X / 2), u8g2.getDisplayHeight() / 2 - (notificationStep * STEP_Y / 2), notificationStep * STEP_X, notificationStep * STEP_Y);
        u8g2.setDrawColor(0x1);
    } else {
        u8g2.setDrawColor(0x0);
        u8g2.drawBox(XY_DIST, XY_DIST, u8g2.getDisplayWidth() - 2 * XY_DIST, u8g2.getDisplayHeight() - 2 * XY_DIST);
        u8g2.setDrawColor(0x1);  //white
        u8g2.drawFrame(XY_DIST, XY_DIST, u8g2.getDisplayWidth() - 2 * XY_DIST, u8g2.getDisplayHeight() - 2 * XY_DIST);

        u8g2.setFont(FONT_NORMAL_HEADING);

        sprintf_P(buff, STR_FUSE_TRIGGERED_HEADING);
        int strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, XY_DIST + u8g2.getMaxCharHeight(), buff);
        int y = u8g2.getMaxCharHeight() + 2 + XY_DIST;
        u8g2.drawHLine(XY_DIST, y, u8g2.getDisplayWidth() - 2 * XY_DIST);

        u8g2.setFont(FONT_NORMAL_TEXT);

        y += u8g2.getMaxCharHeight() + 2;
        sprintf_P(buff, STR_CURRENT_LIMIT1);
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

        y += u8g2.getMaxCharHeight() + 2;
        sprintf_P(buff, STR_CURRENT_LIMIT2, out_name);
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);

        y += u8g2.getMaxCharHeight() + 4;
        sprintf_P(buff, STR_CURRENT_LIMIT3);
        strW = u8g2.getStrWidth(buff);
        u8g2.drawStr(u8g2.getDisplayWidth() / 2 - strW / 2, y, buff);
    }
}

void C_UI_Graphics::DrawMenu_FuseNotifications() {
    static const uint32_t SHOW_TIME = 4000;
    static uint32_t showStart = 0;
    static int displayingErrorForId = -1;

    for (int i = 0; i < N_SUPPLIES; ++i) {
        if (displayingErrorForId == i || displayingErrorForId == -1) {
            if (PowerSupplies[i]->IsFuseTriggered()) {
                displayingErrorForId = i;
                if (showStart == 0) showStart = millis();

                DrawMenu_FuseTriggeredNotification(PowerSupplies_ShortNames[i]);
                if ((millis() - showStart) >= SHOW_TIME) {
                    showStart = 0;
                    displayingErrorForId = -1;
                    notificationStep = 0;
                    PowerSupplies[i]->ClearTriggeredFlag();
                }
            }
        }
    }
}

void C_UI_Graphics::Render() {
    notificationRendered = false;

    u8g2.firstPage();
    do {
        if (laptopMenuState == laptopMenuState_t::MAIN_MENU) {
            if (menuPos == 0)
                DrawMenu_Totals();
            else if (menuPos == MENU_N_ITEMS - 1)
                DrawMenu_About();
            else
                DrawMenu_Normal(menuPos - 1);
        } else if (laptopMenuState == laptopMenuState_t::LAPTOP_BRANDS) {
            DrawMenu_Laptop();
        } else if (laptopMenuState == laptopMenuState_t::LAPTOP_VOLTAGE) {
            DrawMenu_Laptop();
            DrawMenu_LaptopVotageNotification();
        }

        DrawMenu_FuseNotifications();
    } while (u8g2.nextPage());

    if (notificationRendered) {
        if (notificationStep < NUM_STEPS) notificationStep++;
    } else
        notificationStep = 0;
}

void C_UI_Graphics::Navigation() {
    static bool lastEncButton = 0;
    if (Panel.IsEncoderButtonDown() && (Panel.IsEncoderButtonDown() != lastEncButton)) {
        if (menuPos == 2) {
            if (laptopMenuState == laptopMenuState_t::MAIN_MENU)
                laptopMenuState = laptopMenuState_t::LAPTOP_BRANDS;
            else if (laptopMenuState == laptopMenuState_t::LAPTOP_BRANDS) {
                if (selectedLaptopID == 0)
                    laptopMenuState = laptopMenuState_t::LAPTOP_VOLTAGE;
                else {
                    selectedLaptopVoltage = laptopVoltagesDB[selectedLaptopID];
                    Power_Laptop.RequestVoltage(laptopVoltagesDB[selectedLaptopID]);
                    laptopMenuState = laptopMenuState_t::MAIN_MENU;
                }
            } else if (laptopMenuState == laptopMenuState_t::LAPTOP_VOLTAGE) {
                Power_Laptop.RequestVoltage(selectedLaptopVoltage);
                laptopMenuState = laptopMenuState_t::MAIN_MENU;
            }
        }
    }
    lastEncButton = Panel.IsEncoderButtonDown();

    static int lastEnc = 0;

    int encDiff = Panel.GetEncoderPos() - lastEnc;
    lastEnc = Panel.GetEncoderPos();

    if (laptopMenuState == laptopMenuState_t::MAIN_MENU) {
        menuPos += encDiff;
        menuPos = constrain(menuPos, 0, MENU_N_ITEMS - 1);
    } else if (laptopMenuState == laptopMenuState_t::LAPTOP_BRANDS) {
        selectedLaptopID += encDiff;
        selectedLaptopID = constrain(selectedLaptopID, 0, LAPTOP_DB_LEN - 1);
        selectedLaptopVoltage = laptopVoltagesDB[selectedLaptopID];
    } else if (laptopMenuState == laptopMenuState_t::LAPTOP_VOLTAGE) {
        selectedLaptopVoltage += 0.1f * (float)encDiff;
        selectedLaptopVoltage = constrain(selectedLaptopVoltage, C_PwrSupply_Laptop::LAPTOP_MIN_V, C_PwrSupply_Laptop::LAPTOP_MAX_V);
    }
}
