#include "stm32c0xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

UART_HandleTypeDef huart1;
ADC_HandleTypeDef hadc1; // Déclaration de l'ADC1

// Prototypes des fonctions
void UART_Init(void);
void ADC1_Init(void);
void GPIO_Init(void);
void Task_SensorRead(void *pvParameters);
void Task_ValveControl(void *pvParameters);
void Task_EmergencyStop(void *pvParameters);
void Task_AdjustThreshold(void *pvParameters);

// Variables globales
QueueHandle_t xSensorQueue;
SemaphoreHandle_t xEmergencySemaphore;
SemaphoreHandle_t xMutexThreshold;
uint16_t humidityThreshold = 500; // Valeur initiale du seuil d'humidité

// Fonction UART_Init
void UART_Init(void) {
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 9600;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&huart1) != HAL_OK) {
        while (1); // Boucle d'erreur
    }
}

// Fonction pour envoyer des messages via UART
void UART_Transmit(const char *message) {
    HAL_UART_Transmit(&huart1, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
}

// Fonction ADC1_Init
void ADC1_Init(void) {
    __HAL_RCC_ADC_CLK_ENABLE(); // Activez l'horloge ADC

    hadc1.Instance = ADC1; // Utilisez ADC1
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;

    if (HAL_ADC_Init(&hadc1) != HAL_OK) {
        UART_Transmit("Erreur lors de l'initialisation de l'ADC\n");
        while (1); // Boucle d'erreur
    }

    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_3; // Adaptez selon le pin utilisé
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5; // Temps d'échantillonnage

    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
        UART_Transmit("Erreur lors de la configuration du canal ADC\n");
        while (1); // Boucle d'erreur
    }
}

// Fonction GPIO_Init
void GPIO_Init(void) {
    // Initialisation des GPIOs utilisés pour les vannes et boutons
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    // GPIO pour les vannes
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    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);

    // GPIO pour le bouton d'arrêt d'urgence
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

// Tâche : Lire les capteurs d'humidité
void Task_SensorRead(void *pvParameters) {
    uint16_t sensorValue1 = 0;
    uint16_t sensorValue2 = 0;

    for (;;) {
        // Simulation des valeurs des capteurs
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
            sensorValue1 = HAL_ADC_GetValue(&hadc1); // Lecture du capteur 1
        }

        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
            sensorValue2 = HAL_ADC_GetValue(&hadc1); // Lecture du capteur 2
        }

        // Si l'humidité est en dessous du seuil, envoyer un message à la queue
        if (sensorValue1 < humidityThreshold) {
            xQueueSend(xSensorQueue, &sensorValue1, portMAX_DELAY);
            UART_Transmit("Arrosage activé pour Zone 1\n");
        }

        if (sensorValue2 < humidityThreshold) {
            xQueueSend(xSensorQueue, &sensorValue2, portMAX_DELAY);
            UART_Transmit("Arrosage activé pour Zone 2\n");
        }

        vTaskDelay(pdMS_TO_TICKS(5000)); // Attendre 5 secondes
    }
}

// Tâche : Contrôler les vannes
void Task_ValveControl(void *pvParameters) {
    uint16_t receivedValue;

    for (;;) {
        if (xQueueReceive(xSensorQueue, &receivedValue, portMAX_DELAY)) {
            // Simuler l'ouverture de la vanne
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // Activer la vanne
            UART_Transmit("Vanne activée\n");

            vTaskDelay(pdMS_TO_TICKS(10000)); // Maintenir ouverte 10 secondes

            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Désactiver la vanne
            UART_Transmit("Vanne désactivée\n");
        }
    }
}

// Tâche : Gérer l'arrêt d'urgence
void Task_EmergencyStop(void *pvParameters) {
    for (;;) {
        if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == GPIO_PIN_SET) { // Bouton d'arrêt appuyé
            UART_Transmit("Arrêt d'urgence activé\n");
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Fermer toutes les vannes
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);

            xSemaphoreGive(xEmergencySemaphore); // Déclencher l'événement d'urgence
        }

        vTaskDelay(pdMS_TO_TICKS(100)); // Vérifier toutes les 100 ms
    }
}

// Tâche : Ajuster le seuil d'humidité
void Task_AdjustThreshold(void *pvParameters) {
    uint16_t potentiometerValue;

    for (;;) {
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
            potentiometerValue = HAL_ADC_GetValue(&hadc1); // Lecture du potentiomètre

            if (xSemaphoreTake(xMutexThreshold, portMAX_DELAY)) {
                humidityThreshold = potentiometerValue;
                xSemaphoreGive(xMutexThreshold);
                UART_Transmit("Seuil d'humidité mis à jour\n");
            }
        }

        vTaskDelay(pdMS_TO_TICKS(2000)); // Attendre 2 secondes
    }
}

// Fonction vApplicationIdleHook (ne nécessite pas loop())
void vApplicationIdleHook(void) {
    // Cette fonction est appelée lorsque le processeur est inactif
    // Elle est nécessaire pour éviter l'erreur liée à "loop"
}

// Définir une fonction vide "loop" pour satisfaire STM32duino
void loop(void) {
    // La fonction loop() est requise par STM32duino mais nous ne l'utilisons pas
}

// Fonction principale
// Fonction principale
int main(void) {
    HAL_Init();
    UART_Init();
    ADC1_Init();
    GPIO_Init();

    // Création des objets FreeRTOS
    xSensorQueue = xQueueCreate(5, sizeof(uint16_t));
    xEmergencySemaphore = xSemaphoreCreateBinary();
    xMutexThreshold = xSemaphoreCreateMutex();

    // Création des tâches
    xTaskCreate(Task_SensorRead, "Sensor Read", 128, NULL, 1, NULL);
    xTaskCreate(Task_ValveControl, "Valve Control", 128, NULL, 1, NULL);
    xTaskCreate(Task_EmergencyStop, "Emergency Stop", 128, NULL, 2, NULL);
    xTaskCreate(Task_AdjustThreshold, "Adjust Threshold", 128, NULL, 1, NULL);

    // Démarrage du scheduler FreeRTOS
    vTaskStartScheduler(); // Cette fonction démarre le gestionnaire de tâches FreeRTOS

    // Cette ligne ne sera normalement jamais atteinte, car le scheduler prend le contrôle
    for (;;) {}

    }