#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "outputs.h"

static const char *TAG = "smart-oil-heater-control-system-outputs";
const uint8_t uCirculationPumpGpioPin = 27U;
const uint8_t uBurnerGpioPin = 14U;
const uint8_t uSafetyContactGpioPin = 12U;

static SemaphoreHandle_t xMutexAccessOutputs = NULL;
static eOutput sCirculationPumpState;
static eOutput sBurnerState;
static eOutput sSafetyContactState;

void initOutputs(void)
{
    gpio_config_t ioConfCirculationPump = {
        .pin_bit_mask = (1ULL << uCirculationPumpGpioPin), // Pin mask
        .mode = GPIO_MODE_OUTPUT,                          // Set as output
        .pull_up_en = GPIO_PULLUP_DISABLE,                 // Disable pull-up
        .pull_down_en = GPIO_PULLDOWN_DISABLE,             // Disable pull-down
        .intr_type = GPIO_INTR_DISABLE                     // Disable interrupts
    };

    gpio_config_t ioConfBurner = {
        .pin_bit_mask = (1ULL << uBurnerGpioPin), // Pin mask
        .mode = GPIO_MODE_OUTPUT,                 // Set as output
        .pull_up_en = GPIO_PULLUP_DISABLE,        // Disable pull-up
        .pull_down_en = GPIO_PULLDOWN_DISABLE,    // Disable pull-down
        .intr_type = GPIO_INTR_DISABLE            // Disable interrupts
    };

    gpio_config_t ioConfSafetyContact = {
        .pin_bit_mask = (1ULL << uSafetyContactGpioPin), // Pin mask
        .mode = GPIO_MODE_OUTPUT,                        // Set as output
        .pull_up_en = GPIO_PULLUP_DISABLE,               // Disable pull-up
        .pull_down_en = GPIO_PULLDOWN_DISABLE,           // Disable pull-down
        .intr_type = GPIO_INTR_DISABLE                   // Disable interrupts
    };

    gpio_config(&ioConfCirculationPump);
    gpio_config(&ioConfBurner);
    gpio_config(&ioConfSafetyContact);

    xMutexAccessOutputs = xSemaphoreCreateRecursiveMutex();
    if (xMutexAccessOutputs == NULL)
    {
        ESP_LOGE(TAG, "Unable to create mutex");
    }
    xSemaphoreGiveRecursive(xMutexAccessOutputs);
}

eOutput getCirculationPumpState(void)
{
    return sCirculationPumpState;
}

void setCirculationPumpState(eOutput in)
{
    if (xSemaphoreTakeRecursive(xMutexAccessOutputs, pdMS_TO_TICKS(5000)) == pdTRUE)
    {
        sCirculationPumpState = in;
        switch (sCirculationPumpState)
        {
        case ENABLED:
            gpio_set_level(uCirculationPumpGpioPin, 0U); // Switch on Circulation Pump
            break;
        case DISABLED:
            gpio_set_level(uCirculationPumpGpioPin, 1U); // Switch off Circulation Pump
        default:
            break;
        }
        xSemaphoreGiveRecursive(xMutexAccessOutputs);
    }
    else
    {
        ESP_LOGE(TAG, "Unable to take mutex: setCirculationPumpState()");
    }
}

eOutput getBurnerState(void)
{
    eOutput ret = ENABLED;
    if (xSemaphoreTakeRecursive(xMutexAccessOutputs, pdMS_TO_TICKS(5000)) == pdTRUE)
    {
        ret = sBurnerState;
        xSemaphoreGiveRecursive(xMutexAccessOutputs);
    }
    else
    {
        ESP_LOGE(TAG, "Unable to take mutex: getBurnerState()");
    }
    return ret;
}

void setBurnerState(eOutput in)
{
    if (xSemaphoreTakeRecursive(xMutexAccessOutputs, pdMS_TO_TICKS(5000)) == pdTRUE)
    {
        sBurnerState = in;
        switch (sBurnerState)
        {
        case ENABLED:
            gpio_set_level(uBurnerGpioPin, 0U); // Switch on Burner
            break;
        case DISABLED:
            gpio_set_level(uBurnerGpioPin, 1U); // Switch off Burner
        default:
            break;
        }
        xSemaphoreGiveRecursive(xMutexAccessOutputs);
    }
    else
    {
        ESP_LOGE(TAG, "Unable to take mutex: setBurnerState()");
    }
}

eOutput getSafetyControlState(void)
{
    eOutput ret = ENABLED;
    if (xSemaphoreTakeRecursive(xMutexAccessOutputs, pdMS_TO_TICKS(5000)) == pdTRUE)
    {
        ret = sSafetyContactState;
        xSemaphoreGiveRecursive(xMutexAccessOutputs);
    }
    else
    {
        ESP_LOGE(TAG, "Unable to take mutex: getSafetyControlState()");
    }
    return ret;
}

void setSafetyControlState(eOutput in)
{
    if (xSemaphoreTakeRecursive(xMutexAccessOutputs, pdMS_TO_TICKS(5000)) == pdTRUE)
    {
        sSafetyContactState = in;
        switch (sSafetyContactState)
        {
        case ENABLED:
            gpio_set_level(uSafetyContactGpioPin, 0U); // Switch on power for Burner
            break;
        case DISABLED:
            gpio_set_level(uSafetyContactGpioPin, 1U); // Switch off power for Burner
        default:
            break;
        }
        xSemaphoreGiveRecursive(xMutexAccessOutputs);
    }
    else
    {
        ESP_LOGE(TAG, "Unable to take mutex: setSafetyControlState()");
    }
}