#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "stm32c0xx_hal.h"
#include <stdio.h>
#include <stdbool.h>

// Definitions
#define HUMIDITY_THRESHOLD_DEFAULT 2000
#define VALVE_OPEN_DURATION 10000

// GPIO Pins
#define LED_ZONE1_PIN GPIO_PIN_5 // PA5
#define LED_ZONE2_PIN GPIO_PIN_6 // PA6
#define EMERGENCY_LED_PIN GPIO_PIN_7 // PA7
#define BUTTON_PIN GPIO_PIN_2 // PA2

// ADC Channels
#define HUMIDITY1_CHANNEL ADC_CHANNEL_0 // PA0
#define HUMIDITY2_CHANNEL ADC_CHANNEL_1 // PA1
#define THRESHOLD_CHANNEL ADC_CHANNEL_4 // PA4

typedef enum {
    ZONE1,
    ZONE2
} Zone;

QueueHandle_t humidityQueue;
SemaphoreHandle_t emergencySemaphore;
SemaphoreHandle_t thresholdMutex;
ADC_HandleTypeDef hadc1;

uint32_t humidityThreshold = HUMIDITY_THRESHOLD_DEFAULT;
bool emergencyState = false;

// Prototypes
void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_ADC_Init(void);
uint32_t Read_ADC(uint32_t channel);
void Log(const char* msg);

// Tasks
void HumiditySensorTask(void *pvParameters) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    for (;;) {
        if (!emergencyState) {
            uint32_t hum1 = Read_ADC(HUMIDITY1_CHANNEL);
            uint32_t hum2 = Read_ADC(HUMIDITY2_CHANNEL);

            xSemaphoreTake(thresholdMutex, portMAX_DELAY);
            uint32_t threshold = humidityThreshold;
            xSemaphoreGive(thresholdMutex);

            if (hum1 < threshold) {
                Zone zone = ZONE1;
                xQueueSend(humidityQueue, &zone, 0);
            }
            if (hum2 < threshold) {
                Zone zone = ZONE2;
                xQueueSend(humidityQueue, &zone, 0);
            }
        }
        vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(5000));
    }
}

void ValveControlTask(void *pvParameters) {
    Zone zone;
    for (;;) {
        if (xQueueReceive(humidityQueue, &zone, portMAX_DELAY) && !emergencyState) {
            switch (zone) {
                case ZONE1:
                    HAL_GPIO_WritePin(GPIOA, LED_ZONE1_PIN, GPIO_PIN_SET);
                    Log("Vanne zone 1 ouverte.");
                    vTaskDelay(pdMS_TO_TICKS(VALVE_OPEN_DURATION));
                    HAL_GPIO_WritePin(GPIOA, LED_ZONE1_PIN, GPIO_PIN_RESET);
                    Log("Vanne zone 1 fermee.");
                    break;

                case ZONE2:
                    HAL_GPIO_WritePin(GPIOA, LED_ZONE2_PIN, GPIO_PIN_SET);
                    Log("Vanne zone 2 ouverte.");
                    vTaskDelay(pdMS_TO_TICKS(VALVE_OPEN_DURATION));
                    HAL_GPIO_WritePin(GPIOA, LED_ZONE2_PIN, GPIO_PIN_RESET);
                    Log("Vanne zone 2 fermee.");
                    break;
            }
        }
    }
}

void EmergencyTask(void *pvParameters) {
    for (;;) {
        if (HAL_GPIO_ReadPin(GPIOA, BUTTON_PIN) == GPIO_PIN_RESET) { // Bouton pressé
            emergencyState = true;
            HAL_GPIO_WritePin(GPIOA, LED_ZONE1_PIN | LED_ZONE2_PIN, GPIO_PIN_RESET);
            HAL_GPIO_WritePin(GPIOA, EMERGENCY_LED_PIN, GPIO_PIN_SET);
            Log("Arrêt d'urgence activé !");
            xSemaphoreGive(emergencySemaphore);
            vTaskDelay(pdMS_TO_TICKS(1000)); // Anti-rebond
        }
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void ThresholdUpdateTask(void *pvParameters) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    for (;;) {
        uint32_t newThreshold = Read_ADC(THRESHOLD_CHANNEL);
        xSemaphoreTake(thresholdMutex, portMAX_DELAY);
        humidityThreshold = newThreshold;
        xSemaphoreGive(thresholdMutex);
        Log("Seuil d'humidité mis à jour.");
        vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(3000));
    }
}

// ADC Reading Function
uint32_t Read_ADC(uint32_t channel) {
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = channel;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
    uint32_t val = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);
    return val;
}

void Log(const char* msg) {
    printf("%s\n", msg);
}

void loop() {
    // Fonction vide pour compatibilité
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC_Init();

    humidityQueue = xQueueCreate(5, sizeof(Zone));
    emergencySemaphore = xSemaphoreCreateBinary();
    thresholdMutex = xSemaphoreCreateMutex();

    xTaskCreate(HumiditySensorTask, "Humidite", 128, NULL, 2, NULL);
    xTaskCreate(ValveControlTask, "Vannes", 128, NULL, 2, NULL);
    xTaskCreate(EmergencyTask, "Urgence", 128, NULL, 3, NULL);
    xTaskCreate(ThresholdUpdateTask, "Seuil", 128, NULL, 1, NULL);

    vTaskStartScheduler();
    while (1) {}
}

// Clock Configuration (à compléter selon besoin)
void SystemClock_Config(void) {
}

// GPIO Init
void MX_GPIO_Init(void) {
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // LEDs
    GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // Bouton
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// ADC Init
void MX_ADC_Init(void) {
    __HAL_RCC_ADC_CLK_ENABLE();
    hadc1.Instance = ADC1;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;
    HAL_ADC_Init(&hadc1);
}