/**
 * @file actuators.cpp
 * @author Radim Buráň (xburan03@stud.fit.vutbr.cz)
 */

#include "actuators.hpp"

extern presetData_t PRESET_DATA;
extern EventGroupHandle_t STATUS_GROUP;

void pumpWater(long milliliters) {
    long millis = milliliters * 40; //40ms per 1ml
    ESP_LOGI(LOG_TAG, "Pump start for %ldms", millis);
    gpio_set_level(PUMP_PIN, 1);

    vTaskDelay(millis / portTICK_PERIOD_MS);

    ESP_LOGI(LOG_TAG, "Pump stop!");
    gpio_set_level(PUMP_PIN, 0);
}

i2c_dev_t* initPWMDriver() {
    i2c_dev_t* pca9685 = (i2c_dev_t*)calloc(1, sizeof(i2c_dev_t));
    if (pca9685_init_desc(pca9685, 0x40, 0, I2C_SDA_PIN, I2C_SCL_PIN) != ESP_OK) {
        return NULL;
    }
    if (pca9685_init(pca9685) != ESP_OK) {
        return NULL;
    }
    pca9685_restart(pca9685);
    pca9685_set_pwm_frequency(pca9685, 1500);
    return pca9685;
}

void updateLights(i2c_dev_t* sensor, sensorsData_t* sensorData) {
    pca9685_set_pwm_value(sensor, PWM_LED_UV, map(PRESET_DATA.ledUV, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_BLUE, map(PRESET_DATA.ledBlue, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_GREEN, map(PRESET_DATA.ledGreen, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_YELLOW, map(PRESET_DATA.ledYellow, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_RED, map(PRESET_DATA.ledRed, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_FAR_RED, map(PRESET_DATA.ledDeepRed, 0, 100, 0, 4096));
    pca9685_set_pwm_value(sensor, PWM_LED_IR, map(PRESET_DATA.ledIR, 0, 100, 0, 4096));

    pca9685_free_desc(sensor);
}

void disableLights(i2c_dev_t* sensor) {
    pca9685_set_pwm_value(sensor, PWM_LED_UV, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_BLUE, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_GREEN, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_YELLOW, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_RED, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_FAR_RED, 0);
    pca9685_set_pwm_value(sensor, PWM_LED_IR, 0);
}

/**
 * @brief Activate all actuators
 * 
 * @param sensorData data from sensors
 * @param onlineMode device is connected to WiFi
 */
void activateActuators(sensorsData_t* sensorData, bool onlineMode) {
    uint8_t humidity = onlineMode ? PRESET_DATA.humidity : readU8("pump_hum");
    uint8_t hysteresis = onlineMode ? PRESET_DATA.hysteresis : readU8("pump_hyst");
    uint8_t pumpCycle = onlineMode ? PRESET_DATA.pumpCycle : readU8("pump_cycle");

    i2c_dev_t* pwmDriver = initPWMDriver();
    if (sensorData->soilHumidity < humidity - hysteresis) {
        if (sensorData->waterTankLevel > 0) {
            if (onlineMode) {
                uint64_t lastWithOffset = readU64("pump_t") + HOUR_MS;
                if (PRESET_DATA.date >= lastWithOffset) {
                    disableLights(pwmDriver);                  //disable lights to have more power for pump
                    vTaskDelay(200 / portTICK_PERIOD_MS);
                    writeU64("pump_t", PRESET_DATA.date);
                    pumpWater(pumpCycle);
                }
                else {
                    ESP_LOGI(LOG_TAG, "Watered in less than a hour!");
                }
            }
            else {
                uint8_t currentQuarter = readU8("offline_tick");    //used to limit time between watering checks when ESP32 is offline
                ++currentQuarter;
                if (currentQuarter > 4) {
                    vTaskDelay(200 / portTICK_PERIOD_MS);
                    pumpWater(pumpCycle);
                    currentQuarter =  0;
                }
                else {
                    ESP_LOGI(LOG_TAG, "Offline watering is waiting!");
                }
                writeU8("offline_tick", currentQuarter);
            }
        }
        else {
            ESP_LOGI(LOG_TAG, "Water tank is empty!");
        }
    }
    else {
        ESP_LOGI(LOG_TAG, "Soil is humid enough. Not watering!");
    }

    if (onlineMode) {
        updateLights(pwmDriver, sensorData);
    }
    else {
        disableLights(pwmDriver);
    }

    free(sensorData);

    xEventGroupSetBits(STATUS_GROUP, BIT_ACTUATORS_FINISHED);
}