#include <ise_display.hpp>
ISEDisplay::ISEDisplay(HardwareSerial *adapter, const uint8_t *light_array, uint8_t row, uint8_t column) : ESPMegaDisplay(adapter, 115200, 912600, 4, 17)
{
    this->light_array = light_array;
    this->row = row;
    this->column = column;
}
//  Work left
//  TODO : Implement
//  debug to work

void ISEDisplay::begin(DigitalInputCard *inputCard, DigitalOutputCard *outputCard, ClimateCard *climateCard, RemoteVariable* pm_switch, RemoteVariable* pm_fan_speed)
{
    this->inputCard = inputCard;
    this->outputCard = outputCard;
    this->climateCard = climateCard;
    this->pm_switch = pm_switch;
    this->remote_pm_fan_speed = pm_fan_speed;
    auto bindedHandlePWMChange = std::bind(&ISEDisplay::handlePWMChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    auto bindedHandleACChange = std::bind(&ISEDisplay::handleACChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    auto bindedHandleTouch = std::bind(&ISEDisplay::handleTouch, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    this->outputCallbackHandle = this->outputCard->registerChangeCallback(bindedHandlePWMChange);
    this->climateCallbackHandle = this->climateCard->registerChangeCallback(bindedHandleACChange);
    this->user_mode = 1; // initialized to cool by default
    this->pm_fan_speed = 10;
    //remote_pm_fan_speed->setValue(pm_fan_speed);
    this->ac_fan_speed = 0;
    this->ac_mode = 0;
    this->ac_temperature = 25;
    this->lightLevelRow1 = 0;
    this->lightLevelRow2 = 0;
    this->lightLevelRow3 = 0;
    this->lightLevelRow4 = 0;
    // this->time_since_last_screen_update = 0;
    this->registerTouchCallback(bindedHandleTouch);
    this->reset();
    delay(1000);
    // TODO : Will the light be on or off when the system is started?, You need to jump to its respective page
    // first jump to main then if no activity jump to standby
    this->jumpToPage(2); // change this back later to 2
    delay(100);
    this->updateAirPurifierState();
    this->updateACState();
    this->updateLightGroupStatePageDashboard();

    // this->outputCard->setValue(1, 0);
    // this->outputCard->setValue(2, 0);
    // this->outputCard->setValue(3, 0);
    // this->outputCard->setValue(4, 0);
    // this->climateCard->setTemperature(ac_temperature);
    // this->climateCard->setFanSpeed(ac_fan_speed);
    // this->climateCard->setMode(ac_mode);
    this->climateCard->setState(ac_mode, ac_fan_speed, ac_temperature);
}
void ISEDisplay::loop()
{
    // Check if there is data in the serial buffer
    // If there is data, process the data
    recieveSerialCommand();

    // Update the time since the last screen update using millis()

    // u_int32_t current_time = millis();

    // if (current_time - this->time_since_last_screen_update > 120000)
    // {
    //     // jump to standby page if there is no activity for 2 minutes
    //     if(this->currentPage != 1){
    //         this->jumpToPage(1);
    //         ESP_LOGI("ISEDisplay", "Jumping to standby page");
    //     }
    // }
}

void ISEDisplay::handleTouch(uint8_t page, uint8_t component, uint8_t touch_type)
{
    ESP_LOGD("ISEDisplay", "Touch detected on page %d, component %d, touch type %d", page, component, touch_type);
    // time_since_last_screen_update = millis(); // update time since last activity
    char buffer[4];
    if (page == PAGE_STANDBY)
    {
        switch (component)
        {
        case COMPONENT_STANDBY_OPEN_ALL_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // TODO : Should you really jump to page 2 here? should't page jumping be handled reactivly?
            //  EX. if atleast one light is on, then jump to active page, else jump to standby page
            //  This will allow page to change correctly when the system is started and when controlled remotely which won't call handleTouch
            // time_since_last_screen_update = millis();
            this->jumpToPage(2);
            // the function of the button is to open the dashboard from standby
            break;
        case COMPONENT_STANDBY_LIGHT_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // TODO : So this does nothing? Shouldn't it turn on the lights?
            // should turn it on now
            toggleLightGroupStateStandby();
            break;
        case COMPONENT_STANDBY_AC_TOGGLE:
            // TODO : What's the expexted behavior of standby? Does it enter standby when the lights are all off and ignore the AC
            //        or does it only enter standby when ac is also off and purifier is off, or is it a timed thing?
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleAC();

            break;
        case COMPONENT_STANDBY_PM_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            togglePMStandby();
            break;
        default:
            break;
        }
    }

    else if (page == PAGE_DASHBOARD)
    {

        switch (component)
        {
        case COMPONENT_LIGHT_MASTER_BUTTON:
            // TODO : this only update the display to match the light, it doesn't toggle the light.
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightGroupState();
            break;
            // TODOlater : can't this be done better with array lookup?
        case COMPONENT_LIGHT_MASTER_LEVEL1_TOUCHPOINT:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            for (uint8_t i = 1; i <= 4; i++)
            {
                setLightLevel(i, 1);
            }
            break;
        case COMPONENT_LIGHT_MASTER_LEVEL2_TOUCHPOINT:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            for (uint8_t i = 1; i <= 4; i++)
            {
                setLightLevel(i, 2);
            }
            break;
        case COMPONENT_LIGHT_MASTER_LEVEL3_TOUCHPOINT:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            for (uint8_t i = 1; i <= 4; i++)
            {
                setLightLevel(i, 3);
            }
            break;
        case COMPONENT_LIGHT_ROW1_SLIDER:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            lightLevelRow1 = this->getNumber("light_r1_slide.val");
            toggleSliderLight(1, lightLevelRow1);
            break;
        case COMPONENT_LIGHT_ROW2_SLIDER:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            lightLevelRow2 = this->getNumber("light_r2_slide.val");
            toggleSliderLight(2, lightLevelRow2);
            break;
        case COMPONENT_LIGHT_ROW3_SLIDER:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            lightLevelRow3 = this->getNumber("light_r3_slide.val");
            toggleSliderLight(3, lightLevelRow3);
            break;
        case COMPONENT_LIGHT_ROW4_SLIDER:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            lightLevelRow4 = this->getNumber("light_r4_slide.val");
            toggleSliderLight(4, lightLevelRow4);
            break;
        case COMPONENT_LIGHT_ROW1_SWITCH:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(1);
            break;
        case COMPONENT_LIGHT_ROW2_SWITCH:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(2);
            break;
        case COMPONENT_LIGHT_ROW3_SWITCH:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(3);
            break;
        case COMPONENT_LIGHT_ROW4_SWITCH:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(4);
            break;
        // TODO : Don't we have fan only mode too? can you really just switch between 0 and 1?
        case COMPONENT_AC_TOGGLE_BUTTON:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleAC();
            break;
        case COMPONENT_AC_MODE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            changeUserACmode();
            break;
        case COMPONENT_AC_FAN_SPEED:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            ac_fan_speed = this->climateCard->getFanSpeed();
            // We have auto, low, mid, high right?, that's 0,1,2,3 a modulo operation of 3 only gives 0,1,2
            // mod 4 should fixed it
            ESP_LOGI("ISEDisplay", "Current AC fan speed: %d", ac_fan_speed);
            ac_fan_speed = (ac_fan_speed + 1) % 4;
            ESP_LOGI("ISEDisplay", "New AC fan speed: %d", ac_fan_speed);
            this->climateCard->setFanSpeed(ac_fan_speed);
            // updateACState();
            break;
        case COMPONENT_AC_TEMP_DOWN_BUTTON:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            this->climateCard->setTemperature(this->climateCard->getTemperature() - 1);
            // updateACState();
            break;
        case COMPONENT_AC_TEMP_UP_BUTTON:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            this->climateCard->setTemperature(this->climateCard->getTemperature() + 1);
            // updateACState();
            break;
        case COMPONENT_PM_TOGGLE_BUTTON:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            togglePM();
            break;
        case COMPONENT_PM_FAN_SPEED_DECREASE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            pm_fan_speed = (int) atof(remote_pm_fan_speed->getValue());
            ESP_LOGI("ISEDisplay", "Current PM fan speed: %d", pm_fan_speed);
            if (pm_fan_speed >= 1 && pm_fan_speed <= 20)
                itoa(pm_fan_speed - 1, buffer, DEC);
                remote_pm_fan_speed->setValue(buffer);
            ESP_LOGI("ISEDisplay", "New PM fan speed: %d", pm_fan_speed);
            //updateAirPurifierState();
            break;
        case COMPONENT_PM_FAN_SPEED_INCREASE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            pm_fan_speed = (int) atof(remote_pm_fan_speed->getValue());
            ESP_LOGI("ISEDisplay", "Current PM fan speed: %d", pm_fan_speed);
            if (pm_fan_speed >= 0 && pm_fan_speed <= 19)
                itoa(pm_fan_speed + 1, buffer, DEC);
                remote_pm_fan_speed->setValue(buffer);
            ESP_LOGI("ISEDisplay", "New PM fan speed: %d", pm_fan_speed);
            //updateAirPurifierState();
            break;
        default:
            break;
        }
    }

    else
    {
        return;
    }
}

void ISEDisplay::handlePWMChange(uint8_t pin, bool state, uint16_t value)
{
    // NEED to switch case for different page

    uint8_t current_page = this->currentPage;
    if (current_page == PAGE_STANDBY)
    {
        if ((pin >= 0 && pin <= 3) ||(pin >= 8 && pin <= 11))
        {
            // Light
            updateLightGroupStatePageStandby();
            // time_since_last_screen_update = millis(); // update time since last activity
        }
    }
    else if (current_page == PAGE_DASHBOARD)
    {
        if ((pin >= 0 && pin <= 3) ||(pin >= 8 && pin <= 11))
        {
            // Light
            updateLightGroupStatePageDashboard();
            // time_since_last_screen_update = millis(); // update time since last activity
        }
    }
    else
    {
        return;
    }
}
void ISEDisplay::updateDateTimeText(rtctime_t time)
{
    if (!this->takeSerialMutex())
        return;
    // Send the time to the display

    this->displayAdapter->printf("time.txt=\"%02d:%02d\"", time.hours, time.minutes);
    this->sendStopBytes();

    this->displayAdapter->printf("date.txt=\"%02d.%02d.%d\"", time.day, time.month, time.year);
    this->sendStopBytes();

    this->giveSerialMutex();
}
// TODO : Implement
// user remote var
// appdeamon
void ISEDisplay::updateWeather(char *weather_string)
{
    // TODO : use remotevar to get weather data from appdaemon and update the display
    u_int8_t weather_code = 68;

    if (strcmp(weather_string, "fair_day") == 0)
    {
        weather_code = 63;
    }
    else if (strcmp(weather_string, "sunny") == 0)
    {
        weather_code = 63;
    }
    else if (strcmp(weather_string, "fair_night") == 0)
    {
        weather_code = 64;
    }
    else if (strcmp(weather_string, "cloudy") == 0)
    {
        weather_code = 65;
    }
    else if (strcmp(weather_string, "clear-day") == 0)
    {
        weather_code = 66;
    }
    else if (strcmp(weather_string, "clear-night") == 0)
    {
        weather_code = 67;
    }
    else if (strcmp(weather_string, "partlycloudy_day") == 0)
    {
        weather_code = 68;
    }
    else if (strcmp(weather_string, "partlycloudy") == 0)
    {
        weather_code = 68;
    }
    else if (strcmp(weather_string, "partlycloudy_night") == 0)
    {
        weather_code = 69;
    }
    else if (strcmp(weather_string, "heavyrain") == 0)
    {
        weather_code = 70;
    }
    else if (strcmp(weather_string, "heavyrainandthunder") == 0)
    {
        weather_code = 71;
    }
    else if (strcmp(weather_string, "rainandthunder") == 0)
    {
        weather_code = 72;
    }
    else if (strcmp(weather_string, "rain") == 0)
    {
        weather_code = 73;
    }
    else if (strcmp(weather_string, "rainy") == 0)
    {
        weather_code = 73;
    }
    else if (strcmp(weather_string, "lightrain") == 0)
    {
        weather_code = 74;
    }
    else if (strcmp(weather_string, "fog") == 0)
    {
        weather_code = 75;
    }
    else
    {
        weather_code = 68;
    }
    ESP_LOGI("ISEDisplay", "Updating weather to: %s (%d)", weather_string, weather_code);

    if (!this->takeSerialMutex())
        return;
    this->displayAdapter->printf("weather_icon.pic=%d", weather_code);
    this->sendStopBytes();
    this->giveSerialMutex();
}
void ISEDisplay::updateTempOutside(float temp_outside)
{
    // TODO : use remotevar to get PM2.5 data from appdaemon and update the display

    // change temp_outside to int then display
    u_int8_t temp_outside_int = (u_int8_t)temp_outside;
    ESP_LOGI("ISEDisplay", "Updating temperature outside to: %d", temp_outside_int);
    if (!this->takeSerialMutex())
        return;
    this->displayAdapter->printf("outside_temp.txt=\"%d\"", temp_outside_int);
    this->sendStopBytes();
    this->giveSerialMutex();
}
void ISEDisplay::updatePMoutside(u_int16_t pm25_outside)
{
    ESP_LOGI("ISEDisplay", "Updating PM2.5 outside to: %d", pm25_outside);
    u_int16_t curPage = this->currentPage;
    if (curPage == 2)
    {
        if (!this->takeSerialMutex())
            return;
        this->displayAdapter->printf("pm_out.txt=\"%d\"", pm25_outside);
        this->sendStopBytes();
        this->giveSerialMutex();
    }
    // TODO : use remotevar to get PM2.5 data from appdaemon and update the display
}
void ISEDisplay::updatePMinside(u_int16_t pm25_inside)
{
    // TODO : get data from HA's Xiaomi air purifier sensor
    ESP_LOGI("ISEDisplay", "Updating PM2.5 inside to: %d", pm25_inside);
    u_int16_t curPage = this->currentPage;
    if (curPage == 2)
    {
        if (!this->takeSerialMutex())
            return;
        this->displayAdapter->printf("pm_in.txt=\"%d\"", pm25_inside);
        this->sendStopBytes();
        this->giveSerialMutex();
    }
}

void ISEDisplay::setPMstate(bool is_pm_on, uint8_t pm_fan_speed)
{
    ESP_LOGI("ISEDisplay", "Setting PM state: %d, fan speed: %d", is_pm_on, pm_fan_speed);
    char buffer[4];
    itoa(pm_fan_speed, buffer, DEC);
    remote_pm_fan_speed->setValue(buffer);
    pm_switch->setValue(is_pm_on ? "1" : "0");
}

void ISEDisplay::setACstate(uint8_t ac_fan_speed, uint8_t ac_mode, uint8_t ac_temperature)
{
    ESP_LOGI("ISEDisplay", "Setting AC state: = fan speed: %d, mode: %d, temperature: %d", ac_fan_speed, ac_mode, ac_temperature);

    //this->climateCard->setFanSpeed(ac_fan_speed);
    ESP_LOGI("ISEDisplay", "AC fan speed set to: %d", ac_fan_speed);
    //this->climateCard->setMode(ac_mode);
    ESP_LOGI("ISEDisplay", "AC mode set to: %d", ac_mode);
    //this->climateCard->setTemperature(ac_temperature);
    ESP_LOGI("ISEDisplay", "AC temperature set to: %d", ac_temperature);
    this->climateCard->setState(ac_mode, ac_fan_speed, ac_temperature);
    // updateACState();
}
void ISEDisplay::toggleLightGroupState()
{
    // Get the current group state
    bool state = calculateLightGroupState();
    ESP_LOGI("ISEDisplay", "Current light group state: %d", state);
    // Toggle light
    for (uint8_t i = 1; i <= 4; i++)
    {
        setLightLevel(i, state ? 0 : 3);
    }
}
void ISEDisplay::toggleLightGroupStateStandby()
{
    // Get the current group state
    bool state = calculateLightGroupState();
    ESP_LOGI("ISEDisplay", "Current light group state: %d", state);
    // Toggle light
    for (uint8_t i = 1; i <= 4; i++)
    {
        setLightLevel(i, state ? 0 : 3);
    }
    // updateLightGroupStatePageStandby();
}
void ISEDisplay::togglePM()
{
    // Get the current group state
    bool state = strcmp(pm_switch->getValue(), "on") == 0;
    ESP_LOGI("ISEDisplay", "Current PM state: %d", state);
    // Toggle the state
    pm_switch->setValue(state ? "off" : "on");
    ESP_LOGI("ISEDisplay", "New PM state: %d", state);
    //updateAirPurifierState();
}
void ISEDisplay::togglePMStandby()
{
    // Get the current group state
    bool state = strcmp(pm_switch->getValue(), "on") == 0;
    ESP_LOGI("ISEDisplay", "Current PM state: %d", state);
    // Toggle the state
    pm_switch->setValue(state ? "off" : "on");
    ESP_LOGI("ISEDisplay", "New PM state: %d", state);
    //updateAirPurifierStateStandby();
}
void ISEDisplay::toggleAC()
{
    // Get the current group state
    uint8_t mode = this->climateCard->getMode();
    // get fan speed and temperature
    uint8_t fan_speed = this->climateCard->getFanSpeed();
    uint8_t temperature = this->climateCard->getTemperature();

    ESP_LOGI("ISEDisplay", "Current AC mode: %d", mode);
    // Toggle the state
    if (mode == 0)
    {
        ESP_LOGI("ISEDisplay", " User mode: %d", user_mode);
        setACstate(fan_speed, user_mode, temperature);
    }
    else
    {
        ESP_LOGI("ISEDisplay", "User mode BEFORE: %d", user_mode);
        // update user mode to new mode
        user_mode = mode;
        // change actual mode to off
        ESP_LOGI("ISEDisplay", "User mode AFTER: %d", user_mode);
        setACstate(fan_speed, 0, temperature);
    }
}
void ISEDisplay::changeUserACmode()
{
    // Get the current group state
    uint8_t mode = this->climateCard->getMode();
    ESP_LOGI("ISEDisplay", "Current actual AC mode: %d", mode);
    // Toggle the state
    // user mode alternate between 1 and 2
    ESP_LOGI("ISEDisplay", "User mode BEFORE: %d", user_mode);
    user_mode = (user_mode) % 2 + 1;
    ESP_LOGI("ISEDisplay", "User mode AFTER: %d", user_mode);
    if (mode != 0)
    {
        // update mode to new mode
        mode = user_mode;
        ESP_LOGI("ISEDisplay", "change actual AC mode to user mode: %d", mode);
    }
    else
    { // ie mode is off
        // do nothing as the state is keep in user_mode
        //  the mode will change to user_mode when turn on by toggleAC()
        ESP_LOGI("ISEDisplay", "do nothing; user mode: %d , actual mode: %d", user_mode, mode);
    }
    updateuserACmode(); // call to update mode part of the display seperately
}
void ISEDisplay::setLightLevel(uint8_t row, uint8_t level)
{
    // Set the light level
    // this->outputCard->setValue(row, level);
    uint8_t primary_pin = *(light_array + 2*(row - 1));
    uint8_t secondary_pin = *(light_array + 2*(row - 1) + 1);

    bool primary = false;
    bool secondary = false;

    switch (level)
    {
    case 0:
        primary = false;
        secondary = false;
        break;
    case 1:
        primary = false;
        secondary = true;
        break;
    case 2:
        primary = true;
        secondary = false;
        break;
    case 3:
        primary = true;
        secondary = true;
        break;
    default:
        break;
    }

    this->outputCard->setState(primary_pin, primary);
    this->outputCard->setState(secondary_pin, secondary);
}

u_int8_t ISEDisplay::getLightLevel(uint8_t row)
{
    u_int8_t lightLevel = 0;

    //lightLevel = this->outputCard->getValue(row);
    uint8_t primary_pin = *(light_array + 2*(row - 1));
    uint8_t secondary_pin = *(light_array + 2*(row - 1) + 1);
    bool primary = this->outputCard->getState(primary_pin);
    bool secondary = this->outputCard->getState(secondary_pin);
    if (primary && secondary)
    {
        lightLevel = 3;
    }
    else if (primary)
    {
        lightLevel = 2;
    }
    else if (secondary)
    {
        lightLevel = 1;
    }
    else
    {
        lightLevel = 0;
    }

    return lightLevel;
}

//change to light with the assignment

lightPosition ISEDisplay::getRowCol(uint8_t pin){
    lightPosition position;
    //uint8_t row = this->row;
    //uint8_t column = this->column;
    //const uint8_t *light_array = this->light_array;
    // should return the row and column of the light from pin in a row by column array pointer pass from main using pointer arithmetic
    for (uint8_t i = 0; i < row*column; i++)
    {
        uint8_t value = *(light_array + i);
        if (value == pin)
        {
            position.row = i / column;
            position.column = i % column;
            return position;
        }
    }
    return position;
}


void ISEDisplay::updateLightGroupStatePageStandby()
{
    // Calculate the state
    bool state = calculateLightGroupState();
    // Send the state to the display
    if (!this->takeSerialMutex())
        return;

    this->displayAdapter->print("s_light_toggle.pic=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_LIGHT_PIC_ON : COMPONENT_STANDBY_LIGHT_PIC_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("s_light_toggle.pic2=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_LIGHT_PIC_ON_PRESSED : COMPONENT_STANDBY_LIGHT_PIC_OFF_PRESSED);
    this->sendStopBytes();

    this->giveSerialMutex();
}
void ISEDisplay::updateLightGroupStatePageDashboard()
{
    // Calculate the state
    bool state = calculateLightGroupState();
    // Send the state to the display
    if (!this->takeSerialMutex())
        return;

    this->displayAdapter->print("light_master.pic=");
    this->displayAdapter->print(state ? COMPONENT_LIGHT_MASTER_ON : COMPONENT_LIGHT_MASTER_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("light_master.pic2=");
    this->displayAdapter->print(state ? COMPONENT_LIGHT_MASTER_ON_PRESSED : COMPONENT_LIGHT_MASTER_OFF_PRESSED);
    this->sendStopBytes();

    for (uint8_t i = 1; i <= 4; i++)
    {
        u_int8_t state = getLightLevel(i);
        switch (state)
        {
        case 0:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_LIGHT_LEVEL_0);
            this->sendStopBytes();
            break;
        case 1:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_LIGHT_LEVEL_1);
            this->sendStopBytes();
            break;
        case 2:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_LIGHT_LEVEL_2);
            this->sendStopBytes();
            break;
        case 3:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_LIGHT_LEVEL_3);
            this->sendStopBytes();
            break;
        default:
            break;
        }
    }

    this->giveSerialMutex();
}
bool ISEDisplay::calculateLightGroupState()
{
    // Check if all lights are on
    bool lightOn = false;
    for (uint8_t i = 1; i <= 4; i++)
    {
        if (getLightLevel(i) != 0)
        {
            lightOn = true;
            break;
        }
    }
    return lightOn;
}

void ISEDisplay::toggleLightIndividual(uint8_t row)
{
    // Get the current state
    uint8_t state = getLightLevel(row);
    if (state != 0)
    {
        state = 0;
    }
    else
    {
        state = 3;
    }
    // Set the state
    setLightLevel(row, state);
}
void ISEDisplay::toggleSliderLight(uint8_t row, uint8_t lightLevel)
{
    // Get the current state
    uint8_t state = 0;

    if (lightLevel < 10)
    {
        state = 0;
    }
    else if (lightLevel < 33)
    {
        state = 1;
    }
    else if (lightLevel <= 66)
    {
        state = 2;
    }
    else if (lightLevel > 66)
    {
        state = 3;
    }
    else
    {
        state = 0;
    }
    // Set the state
    setLightLevel(row, state);
}
void ISEDisplay::updateAirPurifierStateStandby()
{
    // Get the state
    bool state = strcmp(pm_switch->getValue(), "on") == 0;
    // Send the state to the display
    if (!this->takeSerialMutex())
        return;

    this->displayAdapter->print("s_pm_toggle.pic=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_PM_PIC_ON : COMPONENT_STANDBY_PM_PIC_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("s_pm_toggle.pic2=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_PM_PIC_ON_PRESSED : COMPONENT_STANDBY_PM_PIC_OFF_PRESSED);
    this->sendStopBytes();

    this->giveSerialMutex();
}

void ISEDisplay::updateAirPurifierState()
{
    //check for page
    if(currentPage == PAGE_DASHBOARD){
        ESP_LOGI("ISEDisplay", "Updating dashboard");
        // Get the state
        bool state = strcmp(pm_switch->getValue(), "on") == 0;
        ESP_LOGI("ISEDisplay", "Updating air purifier state to: %d", state);
        pm_fan_speed = (int) atof(remote_pm_fan_speed->getValue());
        // Send the state to the display
        if (!this->takeSerialMutex())
        return;

        this->displayAdapter->print("pm_sw.pic=");
        this->displayAdapter->print(state ? COMPONENT_PM_TOGGLE_PIC_ON : COMPONENT_PM_TOGGLE_PIC_OFF);
        this->sendStopBytes();

        this->displayAdapter->print("pm_sw.pic2=");
        this->displayAdapter->print(state ? COMPONENT_PM_TOGGLE_PIC_ON_PRESSED : COMPONENT_PM_TOGGLE_PIC_OFF_PRESSED);
        this->sendStopBytes();

        this->displayAdapter->print("pm_speed.val=");
        this->displayAdapter->print(pm_fan_speed);
        // this->displayAdapter->print("\"");
        this->sendStopBytes();

        this->displayAdapter->print("pm_speed.pco=");
        this->displayAdapter->print(state ? 34486 : 33841);
        this->sendStopBytes();

        this->giveSerialMutex();
    }
    
    else if(currentPage == PAGE_STANDBY){
        ESP_LOGI("ISEDisplay", "Updating standby");
        updateAirPurifierStateStandby();    
    }
}
void ISEDisplay::handleACChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature)
{
    ESP_LOGI("ISEDisplay", "AC state changed: mode: %d, fan speed: %d, temperature: %d", mode, fan_speed, temperature);
    updateACState();
}
void ISEDisplay::updateuserACmode()
{
    if (!this->takeSerialMutex())
        return;
    ESP_LOGI("ISEDisplay", "updating display user AC mode to: %d", user_mode);
    switch (user_mode)
    {
    case 1:
        this->displayAdapter->print("ac_mode.pic=");
        this->displayAdapter->print(COMPONENT_AC_MODE_COOL_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_mode.pic2=");
        this->displayAdapter->print(COMPONENT_AC_MODE_COOL_PIC_PRESSED);
        this->sendStopBytes();
        break;
    case 2:
        this->displayAdapter->print("ac_mode.pic=");
        this->displayAdapter->print(COMPONENT_AC_MODE_FAN_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_mode.pic2=");
        this->displayAdapter->print(COMPONENT_AC_MODE_FAN_PIC_PRESSED);
        this->sendStopBytes();
        break;
    default:
        break;
    }
    this->giveSerialMutex();
}
void ISEDisplay::updateACfanSpeed()
{
    uint8_t fan_speed = this->climateCard->getFanSpeed();
    ESP_LOGI("ISEDisplay", "updating display AC fan speed to: %d", fan_speed);
    if (!this->takeSerialMutex())
        return;
    switch (fan_speed)
    {
    case 0:
        this->displayAdapter->print("ac_speed.pic=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_AUTO_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_speed.pic2=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_AUTO_PIC_PRESSED);
        this->sendStopBytes();
        break;
    case 1:
        this->displayAdapter->print("ac_speed.pic=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_HIGH_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_speed.pic2=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_HIGH_PIC_PRESSED);
        this->sendStopBytes();
        break;
    case 2:
        this->displayAdapter->print("ac_speed.pic=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_MID_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_speed.pic2=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_MID_PIC_PRESSED);
        this->sendStopBytes();
        break;
    case 3:
        this->displayAdapter->print("ac_speed.pic=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_LOW_PIC);
        this->sendStopBytes();
        this->displayAdapter->print("ac_speed.pic2=");
        this->displayAdapter->print(COMPONENT_AC_FAN_MODE_LOW_PIC_PRESSED);
        this->sendStopBytes();
        break;
    default:
        break;
    }
    this->giveSerialMutex();
}
void ISEDisplay::updateACState()
{
    // TODOlater : The cognitive complexity here is so high, maybe break up the method a bit?

    //  Get the state
    uint8_t mode = this->climateCard->getMode();
    uint8_t temperature = this->climateCard->getTemperature();
    if(currentPage != 1){
        if (!this->takeSerialMutex())
            return;

        // Send the state to the display
        if (mode == 0)
        {
            this->displayAdapter->print("ac_temp.pco=");
            this->displayAdapter->print(33841);
            this->sendStopBytes();

            this->displayAdapter->print("ac_temp.pic=");
            this->displayAdapter->print(COMPONENT_AC_STATUS_OFF);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_temp.pco=");
            this->displayAdapter->print(34486);
            this->sendStopBytes();

            this->displayAdapter->print("ac_temp.pic=");
            this->displayAdapter->print(COMPONENT_AC_STATUS_ON);
            this->sendStopBytes();

            user_mode = mode;
        }
        this->displayAdapter->print("ac_sw.pic=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_AC_TOGGLE_PIC_ON : COMPONENT_AC_TOGGLE_PIC_OFF);
        this->sendStopBytes();
        this->displayAdapter->print("ac_sw.pic2=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_AC_TOGGLE_PIC_ON_PRESSED : COMPONENT_AC_TOGGLE_PIC_OFF_PRESSED);
        this->sendStopBytes();
        this->giveSerialMutex();

        updateuserACmode();

        updateACfanSpeed();
        
        if (!this->takeSerialMutex())
            return;
        
        this->displayAdapter->print("ac_temp.val=");
        this->displayAdapter->print(temperature);
        // this->displayAdapter->print("\"");
        this->sendStopBytes();
        this->giveSerialMutex();
    }
    else if (currentPage == 1)
    {
        if (!this->takeSerialMutex())
            return;

        this->displayAdapter->print("s_ac_toggle.pic=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_STANDBY_AC_PIC_ON : COMPONENT_STANDBY_AC_PIC_OFF);
        this->sendStopBytes();

        this->displayAdapter->print("s_ac_toggle.pic2=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_STANDBY_AC_PIC_ON_PRESSED : COMPONENT_STANDBY_AC_PIC_OFF_PRESSED);
        this->sendStopBytes();

        this->giveSerialMutex();
    }
    

    ESP_LOGI("ISEDisplay", "display AC state updated");
}
