#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;
}

void ISEDisplay::begin(DigitalInputCard *inputCard, DigitalOutputCard *outputCard, ClimateCard *climateCard_daikin, ClimateCard *climateCard_york, RemoteVariable *pm_switch, RemoteVariable *pm_fan_speed, RemoteVariable *ac_lock, RemoteVariable *pm_lock, ESPMegaIoT *iot)
{
    this->iot = iot;
    this->inputCard = inputCard;
    this->outputCard = outputCard;
    this->climateCard_daikin = climateCard_daikin;
    this->climateCard_york = climateCard_york;
    this->pm_switch = pm_switch;
    this->pm_lock = pm_lock;
    this->ac_lock = ac_lock;
    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_daikin->registerChangeCallback(bindedHandleACChange);
    // this->climateCallbackHandle = this->climateCard_york->registerChangeCallback(bindedHandleACChange);
    this->user_mode = 2;                                          // initialized to cool by default
    this->ac_lock_state = strcmp(ac_lock->getValue(), "on") == 0; // initialized to get value
    this->pm_lock_state = strcmp(pm_lock->getValue(), "on") == 0; // initialized to get value
    this->pm_fan_speed = 18;
    remote_pm_fan_speed->setIntValue(this->pm_fan_speed);
    // 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_ac_change = 0;
    this->ac_press_pending = false;
    this->time_since_ac_staggered_start_call = 0;
    this->ac_staggered_start_call_pending = false;
    // this->time_since_last_screen_update = 0;
    this->registerTouchCallback(bindedHandleTouch);
    this->reset();
    delay(1000);
    // 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->updateSystemtoggle();
    this->updateAllStandbyToggle();
    this->updateACState();
    this->updateLightGroupStatePageDashboard();

    // intialize AC state
    if (ac_lock_state == false)
    {
        setACstate(ac_fan_speed, ac_mode, ac_temperature); // default to off with temp 25 and fan on Auto
    }
}
void ISEDisplay::loop()
{
    // Check if there is data in the serial buffer
    // If there is data, process the data
    recieveSerialCommand();

    // Check if the AC state has been changed
    if (ac_press_pending && (millis() - time_since_last_ac_change) > 500)
    {
        ESP_LOGI("ISEDisplay", "Sending AC IR code");
        sendACIRcode();
        ESP_LOGI("ISEDisplay", "AC IR code sent");
        ac_press_pending = false;
        ESP_LOGI("ISEDisplay", "AC press pending set to false");
    }
    // check for ac staggered start
    if (ac_staggered_start_call_pending && (millis() - time_since_ac_staggered_start_call) > 3000)
    {
        ESP_LOGI("ISEDisplay", "Sending Staggered AC IR code (York)");
        sendACIRcode();
        ESP_LOGI("ISEDisplay", "AC Staggered AC IR code (York) sent");
        ac_staggered_start_call_pending = false;
        ESP_LOGI("ISEDisplay", "AC staggered start call pending set to false");
    }

    // Check if the MQTT connection has been lost
    static uint32_t last_mqtt_connected_check = 0;
    if (last_mqtt_connected_check == 0)
    {
        last_mqtt_connected_check = millis() + 15000; // Wait 15 seconds before checking
        ESP_LOGD("ISE Display", "Waiting 15 seconds before checking MQTT connection");
    }
    static bool first_disconnect = true;
    // ESP_LOGI("ISEDisplay", "init first disconnect set to true");
    if (millis() - last_mqtt_connected_check > 3000)
    {
        ESP_LOGD("ISE Display", "Checking MQTT Connection, Connection is %s", this->iot->mqttConnected() ? "true" : "false");
        if (!this->iot->mqttConnected())
        {
            ESP_LOGI("ISEDisplay", "MQTT is disconnected");
            if (first_disconnect)
            {
                // When MQTT is disconnected, enter standalone mode
                // A/C lock is lifted
                // PM lock are lifted
                ESP_LOGI("ISEDisplay", "Entering standalone mode");
                this->ac_lock_state = false;
                ESP_LOGI("ISEDisplay", "AC lock state set to false");
                this->pm_lock_state = false;
                ESP_LOGI("ISEDisplay", "PM lock state set to false");
                first_disconnect = false;
                ESP_LOGI("ISEDisplay", "first disconnect set to false");
            }
        }
        else
        {
            if (first_disconnect == false)
            {
                // When MQTT is connected, exit standalone mode
                // A/C lock is set
                // PM lock is set

                ESP_LOGI("ISEDisplay", "Exiting standalone mode");

                first_disconnect = true;
                ESP_LOGI("ISEDisplay", "first disconnect set to true");
                this->ac_lock_state = strcmp(ac_lock->getValue(), "on") == 0;
                ESP_LOGI("ISEDisplay", "AC lock state set to %d", this->ac_lock_state);
                this->pm_lock_state = strcmp(pm_lock->getValue(), "on") == 0;
                ESP_LOGI("ISEDisplay", "PM lock state set to %d", this->pm_lock_state);
                updateACState();
                ESP_LOGI("ISEDisplay", "AC state updated");
                updateAirPurifierState();
                ESP_LOGI("ISEDisplay", "Air purifier state updated");
            }
        }
        last_mqtt_connected_check = millis();
        ESP_LOGI("ISEDisplay", "last mqtt connected check set to current time");
    }
    // ESP_LOGV("ISEDisplay", "this Loop is finish");
}

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);
    char buffer[4];
    if (page == PAGE_STANDBY)
    {
        switch (component)
        {
        case COMPONENT_OBJ_STANDBY_LOGO:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            //this->jumpToPage(2);
            break;
        case COMPONENT_OBJ_STANDBY_BTN_OPEN_ALL_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            //allToggleStandby();
            this->jumpToPage(2);
            // the function of the button is to open the dashboard from standby
            break;
        case COMPONENT_OBJ_STANDBY_BTN_LIGHT_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightGroupStateStandby();
            break;
        case COMPONENT_OBJ_STANDBY_BTN_AC_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }
            toggleAC();
            break;
        case COMPONENT_OBJ_STANDBY_BTN_PM_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            togglePMStandby();
            break;
        default:
            break;
        }
    }
    else if (page == PAGE_DASHBOARD)
    {

        switch (component)
        {
        case COMPONENT_OBJ_DASHBOARD_NUM_AC_TEMP:
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_AC_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;

            // if ac_lock is true do nothing
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }

            toggleAC();
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_AC_MODE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // if ac_lock is true do nothing
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }
            changeUserACmode();
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_AC_FAN_SPEED:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // if ac_lock is true do nothing
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }
            // ac_fan_speed = this->climateCard_daikin->getFanSpeed();
            ac_fan_speed = this->ac_fan_speed;
            // mode= auto, high, mid, low
            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);
            setACstate(ac_fan_speed, ac_mode, ac_temperature);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_AC_TEMP_MINUS:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // if ac_lock is true do nothing
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }
            // ac_temperature = this->climateCard_daikin->getTemperature() - 1;
            ac_temperature = this->ac_temperature - 1;
            setACstate(ac_fan_speed, ac_mode, ac_temperature);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_AC_TEMP_PLUS:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // if ac_lock is true do nothing
            if (this->ac_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
                break;
            }
            // ac_temperature = this->climateCard_daikin->getTemperature() + 1;
            ac_temperature = this->ac_temperature + 1;
            setACstate(ac_fan_speed, ac_mode, ac_temperature);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_PM_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // if pm_lock is true do nothing
            if (this->pm_lock_state == true)
            {
                ESP_LOGI("ISEDisplay", "PM lock is on, do nothing");
                break;
            }
            togglePM();
            break;
        case COMPONENT_OBJ_DASHBOARD_TXT_PM_INSIDE:
            break;
        case COMPONENT_OBJ_DASHBOARD_TXT_PM_OUTSIDE:
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_LIGHT_ROW1:
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_LIGHT_ROW2:
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_LIGHT_ROW3:
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_LIGHT_ROW4:
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_LIGHT_ROW1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(1);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_LIGHT_ROW2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(2);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_LIGHT_ROW3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(3);
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_LIGHT_ROW4:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightIndividual(4);
            break;
        case COMPONENT_OBJ_DASHBOARD_TIMER_TEN_SEC:
            break;
        case COMPONENT_OBJ_DASHBOARD_VAL_TIME_PAST:
            break;
        case COMPONENT_OBJ_DASHBOARD_TIMER_INACTIVE_CHECK:
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_SYSTEM_TOGGLE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // implement system toggle
            // check light on or off
            // check ac on or off
            // check pm on or off
            toggleSystem();
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_PM_SPEED:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // implement pm speed change
            changePMfanspeed();
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_PM_MODE:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // implement pm mode change
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_MASTER_LIGHT:
            break;
        case COMPONENT_OBJ_DASHBOARD_LOGO:
            jumpToPage(1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_PM_LOCK:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // toggle_pm_lock();
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_AC_LOCK:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            // toggle_ac_lock();
            break;
        case COMPONENT_OBJ_DASHBOARD_BTN_LIGHT_MASTER:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            toggleLightGroupState();
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_MASTER_LV1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightGroupState(1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_MASTER_LV2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightGroupState(2);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_MASTER_LV3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightGroupState(3);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW1_LV1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(1, 1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW1_LV2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(1, 2);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW1_LV3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(1, 3);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW2_LV1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(2, 1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW2_LV2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(2, 2);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW2_LV3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(2, 3);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW3_LV1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(3, 1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW3_LV2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(3, 2);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW3_LV3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(3, 3);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW4_LV1:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(4, 1);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW4_LV2:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(4, 2);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOTSPOT_LIGHT_ROW4_LV3:
            if (touch_type != TOUCH_TYPE_RELEASE)
                break;
            setLightLevel(4, 3);
            break;
        case COMPONENT_OBJ_DASHBOARD_HOLDER_PIC_C_DEGREE:
            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;

    // uint8_t first_pin = *(light_array);
    // uint8_t last_pin = *(light_array + 7);

    if (current_page == PAGE_STANDBY)
    {
        if (LIGHT_ROW1_COLUMN1 || LIGHT_ROW1_COLUMN2 || LIGHT_ROW2_COLUMN1 || LIGHT_ROW2_COLUMN2 || LIGHT_ROW3_COLUMN1 || LIGHT_ROW3_COLUMN2 || LIGHT_ROW4_COLUMN1 || LIGHT_ROW4_COLUMN2) // change
        {
            // Light
            updateLightGroupStatePageStandby();
            updateAllStandbyToggle();
            // time_since_last_screen_update = millis(); // update time since last activity
        }
    }
    else if (current_page == PAGE_DASHBOARD)
    {
        if (LIGHT_ROW1_COLUMN1 || LIGHT_ROW1_COLUMN2 || LIGHT_ROW2_COLUMN1 || LIGHT_ROW2_COLUMN2 || LIGHT_ROW3_COLUMN1 || LIGHT_ROW3_COLUMN2 || LIGHT_ROW4_COLUMN1 || LIGHT_ROW4_COLUMN2) // change
        {
            // Light
            updateLightGroupStatePageDashboard();
            updateSystemtoggle();
            // time_since_last_screen_update = millis(); // update time since last activity
        }
    }
    else
    {
        return;
    }
}
void ISEDisplay::updateDateTimeText(rtctime_t time)
{
    if (this->currentPage == 1)
    {
        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::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 ? "on" : "off");
}

void ISEDisplay::setPMlockstate(bool is_pm_lock_on)
{
    ESP_LOGI("ISEDisplay", "Setting PM lock state: %d", is_pm_lock_on);
    pm_lock->setValue(is_pm_lock_on ? "on" : "off");
}

void ISEDisplay::setAClockstate(bool is_ac_lock_on)
{
    ESP_LOGI("ISEDisplay", "Setting AC lock state: %d", is_ac_lock_on);
    ac_lock->setValue(is_ac_lock_on ? "on" : "off");
}

void ISEDisplay::setACstate(uint8_t ac_fan_speed, uint8_t ac_mode, uint8_t ac_temperature)
{
    ESP_LOGI("ISEDisplay", "AC state before change is: fan speed: %d, mode: %d, temperature: %d", this->ac_fan_speed, this->ac_mode, this->ac_temperature);
    ESP_LOGI("ISEDisplay", "Setting AC state in memory to: = fan speed: %d, mode: %d, temperature: %d", ac_fan_speed, ac_mode, ac_temperature);

    this->ac_fan_speed = ac_fan_speed;
    this->ac_mode = ac_mode;
    this->ac_temperature = ac_temperature;
    // check ac_temp is within bound of daikin
    if (this->ac_temperature < DAIKIN_MIN_TEMP)
    {
        this->ac_temperature = DAIKIN_MIN_TEMP;
    }
    else if (this->ac_temperature > DAIKIN_MAX_TEMP)
    {
        this->ac_temperature = DAIKIN_MAX_TEMP;
    }
    this->time_since_last_ac_change = millis();
    this->ac_press_pending = true;
    updateACState();
    updateSystemtoggle();
    updateAllStandbyToggle();
}
void ISEDisplay::sendACIRcode()
{
    uint8_t cur_ac_fan_speed = this->ac_fan_speed;
    uint8_t cur_ac_mode = this->ac_mode;
    uint8_t before_change_ac_mode = this->climateCard_daikin->getMode();
    uint8_t cur_ac_temperature = this->ac_temperature;
    uint8_t york_temp = cur_ac_temperature;
    // this->climateCard->setFanSpeed(ac_fan_speed);
    ESP_LOGI("ISEDisplay", "AC fan speed set to: %d", cur_ac_fan_speed);
    // this->climateCard->setMode(ac_mode);
    ESP_LOGI("ISEDisplay", "AC mode before change is set to: %d", before_change_ac_mode);
    ESP_LOGI("ISEDisplay", "AC mode set to: %d", cur_ac_mode);
    // this->climateCard->setTemperature(ac_temperature);
    ESP_LOGI("ISEDisplay", "AC temperature set to: %d", cur_ac_temperature);
    // check if temp is within range of min and max for york if not set an min or max for only york
    if (ac_staggered_start_call_pending == false)
    {
        ESP_LOGI("ISEDisplay", "AC Staggered start call pending is false");
        this->climateCard_daikin->setState(cur_ac_mode, cur_ac_fan_speed, cur_ac_temperature);
        ESP_LOGI("ISEDisplay", "Daikin AC IR code sent");
    }
    if (cur_ac_temperature < YORK_MIN_TEMP)
    {
        york_temp = YORK_MIN_TEMP;
    }
    else if (cur_ac_temperature > YORK_MAX_TEMP)
    {
        york_temp = YORK_MAX_TEMP;
    }
    if (cur_ac_mode == 3 && ac_staggered_start_call_pending == false)
    {
        ESP_LOGI("ISEDisplay", "AC Staggered start call pending is false");
        this->climateCard_york->setState(0, cur_ac_fan_speed, york_temp);
        ESP_LOGI("ISEDisplay", "York AC IR code sent to off mode due to swtiching to dry on Daikin");
    }
    else
    {
        // want to stagger start york ac when change state from off (mode 0) to on (mode 1,2,3) by 1 sec without delaying other code
        if (before_change_ac_mode == 0 && ac_staggered_start_call_pending == false)
        {
            ESP_LOGI("ISEDisplay", "cur_ac_mode is 0 and ac_staggered_start_call_pending is false");
            this->time_since_ac_staggered_start_call = millis();
            this->ac_staggered_start_call_pending = true;
            ESP_LOGI("ISEDisplay", "AC staggered start call pending set to true and call time set to current time");
        }
        else
        {
            ESP_LOGI("ISEDisplay", "AC Staggered start call pending is true or cur_ac_mode is not 0");
            this->climateCard_york->setState(cur_ac_mode, cur_ac_fan_speed, york_temp);
            ESP_LOGI("ISEDisplay", "York AC IR code sent");
            // wondering if there is a case where the ac_staggered_start_call_pending is not set to false after setState have been called
        }
    }
    // get value of mode fanspeed and temp and print compare to store value
    ESP_LOGI("ISEDisplay", "AC IR code sent");
    ESP_LOGI("ISEDisplay", "NOTE: ac_staggered_start_call_pending is %d", ac_staggered_start_call_pending);
    if (ac_staggered_start_call_pending == true)
    {
        ESP_LOGI("ISEDisplay", "thus the york ac will not be set to current mode if ac_staggered_start_call_pending is true will be trigger after 1 sec");
    }
    ESP_LOGI("ISEDisplay", "Daikin fan speed: %d, Daikin mode: %d, Daikin temperature: %d", cur_ac_fan_speed, cur_ac_mode, cur_ac_temperature);
    ESP_LOGI("ISEDisplay", "York fan speed: %d, York mode: %d, York temperature: %d", cur_ac_fan_speed, cur_ac_mode, york_temp);
    ESP_LOGI("ISEDisplay", "getting value of mode, fanspeed and temp from climate card");
    ESP_LOGI("ISEDisplay", "Daikin fan speed: %d, Daikin mode: %d, Daikin temperature: %d", this->climateCard_daikin->getFanSpeed(), this->climateCard_daikin->getMode(), this->climateCard_daikin->getTemperature());
    ESP_LOGI("ISEDisplay", "York fan speed: %d, York mode: %d, York temperature: %d", this->climateCard_york->getFanSpeed(), this->climateCard_york->getMode(), this->climateCard_york->getTemperature());
}

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);
    }
    // for loop might slow it down maybe? idk
}
void ISEDisplay::setLightGroupState(uint8_t level)
{
    // Get the current group state
    ESP_LOGI("ISEDisplay", "Current light group state: %d", level);
    // Toggle light
    for (uint8_t i = 1; i <= 4; i++)
    {
        setLightLevel(i, level);
    }
}
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 ispmlock = this->pm_lock_state;
    if (ispmlock)
    {
        ESP_LOGI("ISEDisplay", "PM lock is on, do nothing");
        return;
    }
    else
    {
        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::toggleACLock()
{
    // Get the current group state
    bool state = strcmp(ac_lock->getValue(), "on") == 0;
    ESP_LOGI("ISEDisplay", "Current AC lock state: %d", state);
    // Toggle the state
    ac_lock->setValue(state ? "off" : "on");
    ESP_LOGI("ISEDisplay", "New AC lock state: %d", state);
    // updateAirPurifierStateStandby();
}
void ISEDisplay::togglePMLock()
{
    // Get the current group state
    bool state = strcmp(pm_lock->getValue(), "on") == 0;
    ESP_LOGI("ISEDisplay", "Current PM lock state: %d", state);
    // Toggle the state
    pm_lock->setValue(state ? "off" : "on");
    ESP_LOGI("ISEDisplay", "New PM lock state: %d", state);
    // updateAirPurifierStateStandby();
}
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_daikin->getMode();
    uint8_t mode = this->ac_mode;
    ESP_LOGI("ISEDisplay", "Current AC mode: %d", mode);
    ESP_LOGI("ISEDisplay", "Current user mode: %d", user_mode);
    // get value of mode, fanspeed and temp and print compare to store value
    ESP_LOGI("ISEDisplay", "Daikin fan speed: %d, Daikin mode: %d, Daikin temperature: %d", this->climateCard_daikin->getFanSpeed(), this->climateCard_daikin->getMode(), this->climateCard_daikin->getTemperature());
    ESP_LOGI("ISEDisplay", "York fan speed: %d, York mode: %d, York temperature: %d", this->climateCard_york->getFanSpeed(), this->climateCard_york->getMode(), this->climateCard_york->getTemperature());
    // Toggle the state
    if (mode != 0)
    {
        // update mode to new mode
        mode = 0;
        ESP_LOGI("ISEDisplay", "change actual AC mode to off: %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);
        mode = user_mode;
    }
    // get fan speed and temperature
    // uint8_t fan_speed = this->climateCard_daikin->getFanSpeed();
    // uint8_t temperature = this->climateCard_daikin->getTemperature();
    uint8_t fan_speed = this->ac_fan_speed;
    uint8_t temperature = this->ac_temperature;
    ESP_LOGI("ISEDisplay", "Current AC mode: %d", mode);
    // Toggle the state
    setACstate(fan_speed, mode, temperature);
}
void ISEDisplay::changeUserACmode()
{
    // Get the current group state
    uint8_t mode = this->ac_mode;
    // uint8_t mode = this->climateCard_daikin->getMode();
    ESP_LOGI("ISEDisplay", "Current actual AC mode: %d", mode);
    // Toggle the state
    // user mode alternate between 1, 2, and 3
    ESP_LOGI("ISEDisplay", "User mode BEFORE: %d", user_mode);
    // user_mode = (user_mode) % 3 + 1; //loop from 1 to 2 to 3 then back to 1
    //  Cycle user_mode from 2 to 1 to 3
    switch (user_mode)
    {
    case 2: // If current mode is cool (2), change to fan (1)
        user_mode = 1;
        break;
    case 1: // If current mode is fan (1), change to dry (3)
        user_mode = 3;
        break;
    case 3: // If current mode is dry (3), change to cool (2)
    default:
        user_mode = 2;
        break;
    }
    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);
        setACstate(this->ac_fan_speed, mode, this->ac_temperature);
    }
    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", "update just user mode display; user mode: %d , actual mode: %d", user_mode, mode);
        updateuserACmode();
    }

    // updateuserACmode(); // call to update mode part of the display seperately
}
void ISEDisplay::changePMfanspeed()
{
    // Get the current group state
    uint8_t fan_speed = this->pm_fan_speed;
    uint8_t new_fan_speed = fan_speed;
    // uint8_t fan_speed = this->climateCard_york->getFanSpeed();
    ESP_LOGI("ISEDisplay", "Current PM fan speed: %d", fan_speed);
    // Toggle the state
    // fan_speed have 3 state high mid low switch between them
    switch (fan_speed)
    {
    case PM_FAN_SPEED_LOW:
        new_fan_speed = PM_FAN_SPEED_MID;
        break;
    case PM_FAN_SPEED_MID:
        new_fan_speed = PM_FAN_SPEED_HIGH;
        break;
    case PM_FAN_SPEED_HIGH:
        new_fan_speed = PM_FAN_SPEED_LOW;
        break;
    default:
        new_fan_speed = PM_FAN_SPEED_HIGH;
        break;
    }
    ESP_LOGI("ISEDisplay", "New PM fan speed: %d", new_fan_speed);
    this->pm_fan_speed = new_fan_speed;
    ESP_LOGI("ISEDisplay", "Setting PM fan speed in memory to: %d", this->pm_fan_speed);
    remote_pm_fan_speed->setIntValue(this->pm_fan_speed);
    ESP_LOGI("ISEDisplay", "Setting PM fan speed in remote var to: %d", remote_pm_fan_speed->getValueAsInt());
    updateAirPurifierState();
}
void ISEDisplay::setLightLevel(uint8_t row, uint8_t level)
{
    // Set the light level
    // this->outputCard->setValue(row, level);

    // only value between 1 and 4 is valid
    ESP_LOGI("ISEDisplay", "Setting light level for row %d to %d", row, level);

    uint8_t primary_pin = *(light_array + 2 * (row - 1));
    uint8_t secondary_pin = *(light_array + 2 * (row - 1) + 1);

    ESP_LOGI("ISEDisplay", "Primary pin: %d, Secondary pin: %d", primary_pin, secondary_pin);

    bool primary = false;
    bool secondary = false;

    switch (level)
    {
    case 0:
        primary = false;
        secondary = false;
        ESP_LOGI("ISEDisplay", "Primary pin is %d, Secondary pin is %d", primary, secondary);
        break;
    case 1:
        primary = false;
        secondary = true;
        ESP_LOGI("ISEDisplay", "Primary pin is %d, Secondary pin is %d", primary, secondary);
        break;
    case 2:
        primary = true;
        secondary = false;
        ESP_LOGI("ISEDisplay", "Primary pin is %d, Secondary pin is %d", primary, secondary);
        break;
    case 3:
        primary = true;
        secondary = true;
        ESP_LOGI("ISEDisplay", "Primary pin is %d, Secondary pin is %d", primary, secondary);
        break;
    default:
        ESP_LOGE("ISEDisplay", "Invalid light level: %d", level);
        break;
    }

    this->outputCard->setState(primary_pin, primary);
    this->outputCard->setState(secondary_pin, secondary);
    ESP_LOGI("ISEDisplay", "Light level set for row %d", row);
}

u_int8_t ISEDisplay::getLightLevel(uint8_t row)
{
    u_int8_t lightLevel = 0;
    ESP_LOGI("ISEDisplay", "Getting light level for row %d", row);
    // lightLevel = this->outputCard->getValue(row);
    // row can only be between 1 and 4
    uint8_t primary_pin = *(light_array + 2 * (row - 1));
    uint8_t secondary_pin = *(light_array + 2 * (row - 1) + 1);
    ESP_LOGI("ISEDisplay", "Primary pin: %d, Secondary pin: %d", primary_pin, secondary_pin);
    bool primary = this->outputCard->getState(primary_pin);
    bool secondary = this->outputCard->getState(secondary_pin);
    ESP_LOGI("ISEDisplay", "Primary pin state: %d, Secondary pin state: %d", primary, secondary);
    if (primary && secondary)
    {
        lightLevel = 3;
    }
    else if (primary)
    {
        lightLevel = 2;
    }
    else if (secondary)
    {
        lightLevel = 1;
    }
    else
    {
        lightLevel = 0;
    }
    ESP_LOGI("ISEDisplay", "Light level for row %d is %d", row, lightLevel);
    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_PIC_LIGHT_TOGGLE_ON : COMPONENT_STANDBY_PIC_LIGHT_TOGGLE_OFF);
    this->sendStopBytes();

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

    this->giveSerialMutex();
}
void ISEDisplay::updateLightGroupStatePageDashboard()
{
    // Calculate the state
    bool state_master = calculateLightGroupState();
    uint8_t firstState = getLightLevel(1);
    bool allMatch = true;

    // Send the state to the display
    if (!this->takeSerialMutex())
        return;

    this->displayAdapter->print("light_m_sw.pic=");
    this->displayAdapter->print(state_master ? COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_ON : COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("light_m_sw.pic2=");
    this->displayAdapter->print(state_master ? COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_ON_PRESSED : COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_OFF_PRESSED);
    this->sendStopBytes();

    // this->displayAdapter->print("light_m_sw.pic=");
    // this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_ON : COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_OFF);
    // this->sendStopBytes();

    // this->displayAdapter->print("light_m_sw.pic2=");
    // this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_ON_PRESSED : COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_OFF_PRESSED);
    // this->sendStopBytes();

    for (uint8_t i = 1; i <= 4; i++)
    {
        u_int8_t state = getLightLevel(i);

        if (i > 1 && state != firstState)
        {
            allMatch = false;
        }

        switch (state)
        {
        case 0:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_ROW_LEVEL_0);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_OFF);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_OFF_PRESSED);
            this->sendStopBytes();
            break;
        case 1:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_ROW_LEVEL_1);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON_PRESSED);
            this->sendStopBytes();
            break;
        case 2:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_ROW_LEVEL_2);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON_PRESSED);
            this->sendStopBytes();
            break;
        case 3:
            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print(".pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_ROW_LEVEL_3);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON);
            this->sendStopBytes();

            this->displayAdapter->print("light_row");
            this->displayAdapter->print(i);
            this->displayAdapter->print("_sw.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_SWITCH_ON_PRESSED);
            this->sendStopBytes();
            break;
        default:
            break;
        }
    }

    if (allMatch)
    {
        // All states match, change the master light level which is the firststate
        // use switch case
        switch (firstState)
        {
        case 0:
            this->displayAdapter->print("master_light.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_0);
            this->sendStopBytes();
            break;
        case 1:
            this->displayAdapter->print("master_light.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_1);
            this->sendStopBytes();
            break;
        case 2:
            this->displayAdapter->print("master_light.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_2);
            this->sendStopBytes();
            break;
        case 3:
            this->displayAdapter->print("master_light.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_3);
            this->sendStopBytes();
            break;
        default:
            this->displayAdapter->print("master_light.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_D);
            this->sendStopBytes();
            break;
        }
    }
    else
    {
        this->displayAdapter->print("master_light.pic=");
        this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_LIGHT_MASTER_LEVEL_D);
        this->sendStopBytes();
    }

    this->giveSerialMutex();
}
void ISEDisplay::updateLightSwitch()
{
    // DEPRECATED
    //  Calculate the state
    // not in use due to worse performance

    // check state and set for each individual light row
    // if state is 0 set to off and other(1,2,3) to on
}
bool ISEDisplay::calculateLightGroupState()
{
    // Check if all lights are on
    bool lightOn = false;
    // uint_8 first_pin = *(light_array);
    // uint_8 last_pin = *(light_array + 7);
    for (uint8_t i = 1; i <= 4; i++)
    {
        if (getLightLevel(i) != 0)
        {
            lightOn = true;
            break;
        }
    }
    return lightOn;
}
bool ISEDisplay::calculateAllState()
{
    bool lightState = calculateLightGroupState();
    bool pmState = strcmp(pm_switch->getValue(), "on") == 0;
    bool acState = false;
    if (ac_mode != 0)
    {
        acState = true;
    }
    return lightState || pmState || acState;
}
void ISEDisplay::toggleSystem()
{
    toggleLightGroupState();
    // check for ac lock and pm lock, if lock is on do nothing
    if (this->ac_lock_state == true)
    {
        ESP_LOGI("ISEDisplay", "AC lock is on, do nothing");
    }
    else
    {
        toggleAC();
    }
    if (this->pm_lock_state == true)
    {
        ESP_LOGI("ISEDisplay", "PM lock is on, do nothing");
    }
    else
    {
        togglePM();
    }
}
void ISEDisplay::allToggleStandby()
{
    bool lightState = calculateLightGroupState();
    bool pmState = strcmp(pm_switch->getValue(), "on") == 0;
    if (ac_mode != 0)
    {
        setACstate(0, ac_fan_speed, ac_temperature);
    }
    else{
        setACstate(2, ac_fan_speed, ac_temperature);
    }
    if (lightState)
    {
        setLightGroupState(0);
    }
    else
    {
        setLightGroupState(3);
    }
    if (pmState)
    {
        setPMstate(false, pm_fan_speed);
    }
    else
    {
        setPMstate(true, pm_fan_speed);
    }
    
}
void ISEDisplay::updateAllStandbyToggle()
{
    bool state = calculateAllState();
    if (!this->takeSerialMutex())
        return;
    this->displayAdapter->print("s_open_all.pic=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_PIC_OPEN_ALL_TOGGLE_ON : COMPONENT_STANDBY_PIC_OPEN_ALL_TOGGLE_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("s_open_all.pic2=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_PIC_OPEN_ALL_TOGGLE_ON_PRESSED : COMPONENT_STANDBY_PIC_OPEN_ALL_TOGGLE_OFF_PRESSED);
    this->sendStopBytes();

    this->giveSerialMutex();
}
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::updateSystemtoggle()
{
    bool state = calculateAllState();
    if (!this->takeSerialMutex())
        return;
    this->displayAdapter->print("system_toggle.pic=");
    this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_SYSTEM_TOGGLE_ON : COMPONENT_DASHBOARD_PIC_SYSTEM_TOGGLE_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("system_toggle.pic2=");
    this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_SYSTEM_TOGGLE_ON_PRESSED : COMPONENT_DASHBOARD_PIC_SYSTEM_TOGGLE_OFF_PRESSED);
    this->sendStopBytes();

    this->giveSerialMutex();
}
void ISEDisplay::updateAirPurifierStateStandby()
{
    // Get the state
    bool state = strcmp(pm_switch->getValue(), "on") == 0;
    this->pm_lock_state = strcmp(pm_lock->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_PIC_PM_TOGGLE_ON : COMPONENT_STANDBY_PIC_PM_TOGGLE_OFF);
    this->sendStopBytes();

    this->displayAdapter->print("s_pm_toggle.pic2=");
    this->displayAdapter->print(state ? COMPONENT_STANDBY_PIC_PM_TOGGLE_ON_PRESSED : COMPONENT_STANDBY_PIC_PM_TOGGLE_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;
        this->pm_lock_state = strcmp(pm_lock->getValue(), "on") == 0;
        ESP_LOGI("ISEDisplay", "Updating air purifier state to: %d", state);
        pm_fan_speed = this->pm_fan_speed;
        ESP_LOGI("ISEDisplay", "Updating air purifier fan speed to: %d", pm_fan_speed);
        // Send the state to the display
        if (!this->takeSerialMutex())
            return;

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

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

        this->displayAdapter->print("pm_lock.pic=");
        this->displayAdapter->print(this->pm_lock_state ? COMPONENT_DASHBOARD_PIC_LOCK : COMPONENT_DASHBOARD_PIC_UNLOCK);
        this->sendStopBytes();

        this->displayAdapter->print("pm_mode.pic=");
        this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_ON : COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_OFF);
        this->sendStopBytes();

        this->displayAdapter->print("pm_mode.pic2=");
        this->displayAdapter->print(state ? COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_ON_PRESSED : COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_OFF_PRESSED);
        this->sendStopBytes();

        switch (pm_fan_speed)
        {
        case PM_FAN_SPEED_LOW:
            if (state)
            { // state is on
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_ON);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_ON_PRESSED);
                this->sendStopBytes();
            }
            else
            { // state is off
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_OFF);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_OFF_PRESSED);
                this->sendStopBytes();
            }
            break;
        case PM_FAN_SPEED_MID:
            if (state)
            { // state is on
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_ON);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_ON_PRESSED);
                this->sendStopBytes();
            }
            else
            { // state is off
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_OFF);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_OFF_PRESSED);
                this->sendStopBytes();
            }
            break;
        case PM_FAN_SPEED_HIGH:
            if (state)
            { // state is on
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_ON);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_ON_PRESSED);
                this->sendStopBytes();
            }
            else
            { // state is off
                this->displayAdapter->print("pm_speed.pic=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_OFF);
                this->sendStopBytes();

                this->displayAdapter->print("pm_speed.pic2=");
                this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_OFF_PRESSED);
                this->sendStopBytes();
            }
            break;
        default:
            break;
        }

        // 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();
    updateSystemtoggle();
    updateAllStandbyToggle();
}
void ISEDisplay::updateuserACmode()
{
    // uint8_t mode = this->climateCard_daikin->getMode();
    uint8_t mode = this->ac_mode;
    if (!this->takeSerialMutex())
        return;
    ESP_LOGI("ISEDisplay", "updating display user AC mode to: %d", user_mode);
    switch (user_mode)
    {
    case 1:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_FAN_ON_PRESSED);
            this->sendStopBytes();
        }
        break;
    case 2:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_COOL_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_COOL_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_COOL_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_COOL_ON_PRESSED);
            this->sendStopBytes();
        }
        break;

    case 3:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_DRY_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_DRY_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_mode.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_DRY_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_mode.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_MODE_DRY_ON_PRESSED);
            this->sendStopBytes();
        }
        break;
    default:
        break;
    }
    this->giveSerialMutex();
}
void ISEDisplay::updateACfanSpeed()
{
    // uint8_t fan_speed = this->climateCard_daikin->getFanSpeed();
    // uint8_t mode = this->climateCard_daikin->getMode();
    uint8_t fan_speed = this->ac_fan_speed;
    uint8_t mode = this->ac_mode;
    this->ac_lock_state = strcmp(ac_lock->getValue(), "on") == 0;

    ESP_LOGI("ISEDisplay", "updating display AC fan speed to: %d", fan_speed);
    if (!this->takeSerialMutex())
        return;
    switch (fan_speed)
    {
    case 0:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_AUTO_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_AUTO_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_AUTO_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_AUTO_ON_PRESSED);
            this->sendStopBytes();
        }
        break;
    case 1:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_HIGH_ON_PRESSED);
            this->sendStopBytes();
        }
        break;
    case 2:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_MID_ON_PRESSED);
            this->sendStopBytes();
        }
        break;
    case 3:
        if (mode == 0)
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_OFF);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_OFF_PRESSED);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_speed.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_ON);
            this->sendStopBytes();
            this->displayAdapter->print("ac_speed.pic2=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_AC_FAN_LOW_ON_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_daikin->getMode();
    // uint8_t temperature = this->climateCard_daikin->getTemperature();
    uint8_t mode = this->ac_mode;
    uint8_t temperature = this->ac_temperature;
    this->ac_lock_state = strcmp(ac_lock->getValue(), "on") == 0;

    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(COMPONENT_COLOR_INACTIVE_GREY);
            this->sendStopBytes();

            this->displayAdapter->print("c_degree.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_DEGREE_C_OFF);
            this->sendStopBytes();
        }
        else
        {
            this->displayAdapter->print("ac_temp.pco=");
            this->displayAdapter->print(COMPONENT_COLOR_ACTIVE_BLUE);
            this->sendStopBytes();

            this->displayAdapter->print("c_degree.pic=");
            this->displayAdapter->print(COMPONENT_DASHBOARD_PIC_DEGREE_C_ON);
            this->sendStopBytes();
        }
        this->displayAdapter->print("ac_sw.pic=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_DASHBOARD_PIC_AC_TOGGLE_ON : COMPONENT_DASHBOARD_PIC_AC_TOGGLE_OFF);
        this->sendStopBytes();
        this->displayAdapter->print("ac_sw.pic2=");
        this->displayAdapter->print(mode != 0 ? COMPONENT_DASHBOARD_PIC_AC_TOGGLE_ON_PRESSED : COMPONENT_DASHBOARD_PIC_AC_TOGGLE_OFF_PRESSED);
        this->sendStopBytes();
        this->displayAdapter->print("ac_lock.pic=");
        this->displayAdapter->print(this->ac_lock_state ? COMPONENT_DASHBOARD_PIC_LOCK : COMPONENT_DASHBOARD_PIC_UNLOCK);
        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_PIC_AC_TOGGLE_ON : COMPONENT_STANDBY_PIC_AC_TOGGLE_OFF);
        this->sendStopBytes();

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

        this->giveSerialMutex();
    }

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