#include <cud_display.hpp>

CUDDisplay::CUDDisplay(HardwareSerial *adapter) : ESPMegaDisplay(adapter)
{

}

void CUDDisplay::begin(std::function<rtctime_t()> getTime, DigitalInputCard *inputCard,
                        DigitalOutputCard *outputCard, ClimateCard *climateCard)
{
    this->getTime = getTime;
    this->inputCard = inputCard;
    this->outputCard = outputCard;
    this->climateCard = climateCard;
    auto bindedHandlePWMChange = std::bind(&CUDDisplay::handlePWMChange, this, std::placeholders::_1, std::placeholders::_2);
    auto bindedHandleACChange = std::bind(&CUDDisplay::handleACChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    auto bindedHandleTouch = std::bind(&CUDDisplay::handleTouch, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    auto bindedHandlePayload = std::bind(&CUDDisplay::handlePayload, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    this->outputCallbackHandle = this->outputCard->registerChangeCallback(bindedHandlePWMChange);
    this->climateCallbackHandle = this->climateCard->registerChangeCallback(bindedHandleACChange);
    this->registerTouchCallback(bindedHandleTouch);
    this->registerPayloadCallback(bindedHandlePayload);
    this->reset();
    delay(1000);
    this->jumpToPage(1);
    delay(100);
    this->sendClock();
    this->setACControlEnabled(true);
    this->updateLightGroupState();
    this->updateFanGroupState();
    this->updateAirPurifierState();
    this->updateACState();
}

void CUDDisplay::handleTouch(uint8_t page, uint8_t component, uint8_t event)
{
    if (page != PAGE_DASHBOARD)
        return;
    switch (component)
    {
    case COMPONENT_LIGHT_TOGGLE_BUTTON:
        if (event != TOUCH_TYPE_RELEASE)
            break;
        toggleLightGroupState();
        break;
    case COMPONENT_FAN_TOGGLE_BUTTON:
        if (event != TOUCH_TYPE_RELEASE)
            break;
        toggleFanGroupState();
        break;
    case COMPONENT_AIR_PURIFIER_TOGGLE_BUTTON:
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->outputCard->setState(7, !this->outputCard->getState(7));
        break;
    case COMPONENT_AC_TEMP_UP_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setTemperature(this->climateCard->getTemperature() + 1);
        break;
    case COMPONENT_AC_TEMP_DOWN_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setTemperature(this->climateCard->getTemperature() - 1);
        break;
    case COMPONENT_AC_MODE_OFF_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setMode(0);
        break;
    case COMPONENT_AC_MODE_FAN_ONLY_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setMode(1);
        break;
    case COMPONENT_AC_MODE_COOL_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setMode(2);
        break;
    case COMPONENT_AC_FAN_MODE_AUTO_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setFanSpeed(0);
        break;
    case COMPONENT_AC_FAN_MODE_LOW_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setFanSpeed(3);
        break;
    case COMPONENT_AC_FAN_MODE_MEDIUM_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setFanSpeed(2);
        break;
    case COMPONENT_AC_FAN_MODE_HIGH_BUTTON:
        if (!ac_control_enabled) break;
        if (event != TOUCH_TYPE_RELEASE)
            break;
        this->climateCard->setFanSpeed(1);
        break;
    default:
        break;
    }
}

void CUDDisplay::handlePWMChange(uint8_t pin, uint8_t value)
{
    // Is the pin a light, fan, or air purifier?
    if (pin >= 0 && pin <= 3)
    {
        // Light
        updateLightGroupState();
    }
    else if (pin >= 4 && pin <= 6)
    {
        // Fan
        updateFanGroupState();

    }
    else if (pin == 7)
    {
        // Air Purifier
        updateAirPurifierState();
    }
}

void CUDDisplay::handleACChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature)
{
    // Update AC Controls
    updateACState();
}

void CUDDisplay::sendClock()
{
    // Get the time
    rtctime_t time = this->getTime();
    // Send the time to the display
    this->displayAdapter->print("clock.txt=");
    // We use 24 hour format, HH:MM
    this->displayAdapter->print(time.hours);
    this->displayAdapter->print(":");
    this->displayAdapter->print(time.minutes);
    this->sendStopBytes();
}

bool CUDDisplay::calculateLightGroupState() {
    // Check if all lights are on
    bool lightOn = false;
    for (uint8_t i = 0; i < 4; i++) {
        if (this->outputCard->getState(i)) {
            lightOn = true;
            break;
        }
    }
    return lightOn;
}
bool CUDDisplay::calculateFanGroupState() {
    // Check if all fans are on
    bool fanOn = false;
    for (uint8_t i = 4; i < 7; i++) {
        if (this->outputCard->getState(i)) {
            fanOn = true;
            break;
        }
    }
    return fanOn;
}
void CUDDisplay::setLightGrouptState(bool state) {
    // Set all lights to state
    for (uint8_t i = 0; i < 4; i++) {
        this->outputCard->setState(i, state);
    }
}
void CUDDisplay::setFanGroupState(bool state) {
    // Set all fans to state
    for (uint8_t i = 4; i < 7; i++) {
        this->outputCard->setState(i, state);
    }
}
void CUDDisplay::toggleLightGroupState() {
    // Get the current group state
    bool state = calculateLightGroupState();
    // Toggle the state
    state = !state;
    // Set the state
    setLightGrouptState(state);
}
void CUDDisplay::toggleFanGroupState() {
    // Get the current group state
    bool state = calculateFanGroupState();
    // Toggle the state
    state = !state;
    // Set the state
    setFanGroupState(state);
}
void CUDDisplay::updateLightGroupState() {
    // Calculate the state
    bool state = calculateLightGroupState();
    // Send the state to the display
    this->displayAdapter->print("lt_bt.pic=");
    this->displayAdapter->print(state ? COMPONENT_LIGHT_TOGGLE_PIC_ON : COMPONENT_LIGHT_TOGGLE_PIC_OFF);
    this->sendStopBytes();
}
void CUDDisplay::updateFanGroupState() {
    // Calculate the state
    bool state = calculateFanGroupState();
    // Send the state to the display
    this->displayAdapter->print("fan_bt.pic=");
    this->displayAdapter->print(state ? COMPONENT_FAN_TOGGLE_PIC_ON : COMPONENT_FAN_TOGGLE_PIC_OFF);
    this->sendStopBytes();
}
void CUDDisplay::updateAirPurifierState() {
    // Get the state
    bool state = this->outputCard->getState(7);
    // Send the state to the display
    this->displayAdapter->print("puri_bt.pic=");
    this->displayAdapter->print(state ? COMPONENT_AIR_PURIFIER_TOGGLE_PIC_ON : COMPONENT_AIR_PURIFIER_TOGGLE_PIC_OFF);
    this->sendStopBytes();
}
void CUDDisplay::updateACState() {
    // Get the state
    uint8_t mode = this->climateCard->getMode();
    uint8_t fan_speed = this->climateCard->getFanSpeed();
    uint8_t temperature = this->climateCard->getTemperature();
    // Send the state to the display
    this->displayAdapter->print("mode_off_btn.pic=");
    this->displayAdapter->print(mode == 0 ? COMPONENT_AC_MODE_OFF_PIC_ACTIVE : COMPONENT_AC_MODE_OFF_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("mode_fan_btn.pic=");
    this->displayAdapter->print(mode == 1 ? COMPONENT_AC_MODE_FAN_ONLY_PIC_ACTIVE : COMPONENT_AC_MODE_FAN_ONLY_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("mode_cool_btn.pic=");
    this->displayAdapter->print(mode == 2 ? COMPONENT_AC_MODE_COOL_PIC_ACTIVE : COMPONENT_AC_MODE_COOL_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("fan_auto_btn.pic=");
    this->displayAdapter->print(fan_speed == 0 ? COMPONENT_AC_FAN_MODE_AUTO_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_AUTO_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("fan_1_btn.pic=");
    this->displayAdapter->print(fan_speed == 3 ? COMPONENT_AC_FAN_MODE_HIGH_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_HIGH_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("fan_2_btn.pic=");
    this->displayAdapter->print(fan_speed == 2 ? COMPONENT_AC_FAN_MODE_MEDIUM_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_MEDIUM_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("fan_3_btn.pic=");
    this->displayAdapter->print(fan_speed == 1 ? COMPONENT_AC_FAN_MODE_LOW_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_LOW_PIC_INACTIVE);
    this->sendStopBytes();
    this->displayAdapter->print("temp_txt.txt=\"");
    this->displayAdapter->print(temperature);
    this->displayAdapter->print("C\"");
    this->sendStopBytes();
}

void CUDDisplay::setACControlEnabled(bool enabled) {
    ac_control_enabled = enabled;
    this->displayAdapter->print("dashboard.pic=");
    this->displayAdapter->print(enabled ? COMPONENT_BACKGROUND_AC_UNLOCK : COMPONENT_BACKGROUND_AC_LOCK);
    this->sendStopBytes();
}
bool CUDDisplay::getACControlEnabled() {
    return ac_control_enabled;
}

void CUDDisplay::handlePayload(uint8_t payload_type, uint8_t *payload, uint8_t length)
{
    
}