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

#include "inputs.h"

#define MAX_DN18B20_SENSORS 4U

static const char *TAG = "smart-oil-heater-control-system-inputs";
const uint8_t uBurnerFaultPin = 19U;
const uint8_t uDS18B20Pin = 4U;
const onewire_addr_t uChamperTempSensorAddr = 0x3e0000001754be28;
const onewire_addr_t uOutdoorTempSensorAddr = 0x880000001648e328;
const onewire_addr_t uInletFlowTempSensorAddr = 0xe59cdef51e64ff28;
const onewire_addr_t uReturnFlowTempSensorAddr = 0xa7a8e1531f64ff28;

onewire_addr_t uOneWireAddresses[MAX_DN18B20_SENSORS];
float fDS18B20Temps[MAX_DN18B20_SENSORS];
size_t sSensorCount = 0U;

static eBurnerErrorState sBurnerErrorState;
static float fChamperTemperature;
static float fOutdoorTemperature;
static float fInletFlowTemperature;
static float fReturnFlowTemperature;

void taskInput(void *pvParameters);

void initInputs(void)
{

    gpio_config_t ioConfBurnerFault = {
        .pin_bit_mask = (1ULL << uBurnerFaultPin), // Pin mask
        .mode = GPIO_MODE_INPUT,                   // Set as inout
        .pull_up_en = GPIO_PULLUP_ENABLE,          // Enable pull-up
        .pull_down_en = GPIO_PULLDOWN_DISABLE,     // Disable pull-down
        .intr_type = GPIO_INTR_DISABLE             // Disable interrupts
    };

    gpio_config(&ioConfBurnerFault);

    BaseType_t taskCreated = xTaskCreate(
        taskInput,   // Function to implement the task
        "taskInput", // Task name
        2048,        // Stack size (in words, not bytes)
        NULL,        // Parameters to the task function (none in this case)
        5,           // Task priority (higher number = higher priority)
        NULL         // Task handle (optional)
    );

    if (taskCreated == pdPASS)
    {
        ESP_LOGI(TAG, "Task created successfully!");
    }
    else
    {
        ESP_LOGE(TAG, "Failed to create task");
    }
}

void taskInput(void *pvParameters)
{
    while (1)
    {
        ESP_LOGI(TAG, "Running task Input...");
        vTaskDelay(1000U / portTICK_PERIOD_MS);

        if (gpio_get_level(uBurnerFaultPin) == 1)
        {
            sBurnerErrorState = FAULT;
        }
        else
        {
            sBurnerErrorState = NO_ERROR;
        }

        if (ds18x20_scan_devices(uDS18B20Pin, uOneWireAddresses, MAX_DN18B20_SENSORS, &sSensorCount) != ESP_OK)
        {
            ESP_LOGE(TAG, "1-Wire device scan error!");
        }

        if (!sSensorCount)
        {
            ESP_LOGW(TAG, "No 1-Wire devices detected!");
        }
        else
        {
            ESP_LOGI(TAG, "%d 1-Wire devices detected", sSensorCount);

            if (sSensorCount > MAX_DN18B20_SENSORS)
            {
                sSensorCount = MAX_DN18B20_SENSORS;
                ESP_LOGW(TAG, "More 1-Wire devices found than expected!");
            }

            if (ds18x20_measure_and_read_multi(uDS18B20Pin, uOneWireAddresses, sSensorCount, fDS18B20Temps) != ESP_OK)
            {
                ESP_LOGE(TAG, "1-Wire devices read error");
            }
            else
            {
                for (int j = 0; j < sSensorCount; j++)
                {
                    float temp_c = fDS18B20Temps[j];
                    ESP_LOGI(TAG, "Sensor: %08" PRIx64 " reports %.3f°C", (uint64_t)uOneWireAddresses[j], temp_c);

                    switch ((uint64_t)uOneWireAddresses[j])
                    {
                    case ((uint64_t)uChamperTempSensorAddr):
                        fChamperTemperature = temp_c;
                        break;
                    case ((uint64_t)uOutdoorTempSensorAddr):
                        fOutdoorTemperature = temp_c;
                        break;
                    case ((uint64_t)uInletFlowTempSensorAddr):
                        fInletFlowTemperature = temp_c;
                        break;
                    case ((uint64_t)uReturnFlowTempSensorAddr):
                        fReturnFlowTemperature = temp_c;
                        break;
                    default:
                        break;
                    }
                }
            }
        }
    }
}

float getChamberTemperature(void)
{
    return fChamperTemperature;
}
float getOutdoorTemperature(void)
{
    return fOutdoorTemperature;
}
float getInletFlowTemperature(void)
{
    return fInletFlowTemperature;
}
float getReturnFlowTemperature(void)
{
    return fReturnFlowTemperature;
}
eBurnerErrorState getBurnerError(void)
{
    return sBurnerErrorState;
}