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

// GPIO Pins pour les LEDs
#define LED1_PIN GPIO_PIN_5
#define LED2_PIN GPIO_PIN_6
#define LED3_PIN GPIO_PIN_7
#define LED_PORT GPIOA

typedef struct {
    TaskHandle_t taskHandle;
    uint32_t deadline; // Deadline de la tâche
} EDF_Task_t;

// Sémaphore binaire
SemaphoreHandle_t xSemaphore;

// Tableau de tâches EDF
EDF_Task_t edfTasks[3];

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

    GPIO_InitStruct.Pin = LED1_PIN | LED2_PIN | LED3_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}

// Sélection de la tâche avec la deadline la plus proche
EDF_Task_t* SelectTaskWithEarliestDeadline() {
    EDF_Task_t* selectedTask = &edfTasks[0];
    for (int i = 1; i < 3; i++) {
        if (edfTasks[i].deadline < selectedTask->deadline) {
            selectedTask = &edfTasks[i];
        }
    }
    return selectedTask;
}

// Tâches EDF
void Task1(void *pvParameters) {
    for (;;) {
        // Attendre la permission du scheduler
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        // Prendre le sémaphore
        xSemaphoreTake(xSemaphore, portMAX_DELAY);

        HAL_GPIO_WritePin(LED_PORT, LED1_PIN, GPIO_PIN_SET);
        vTaskDelay(pdMS_TO_TICKS(200));  // Temps d'exécution de la LED1
        HAL_GPIO_WritePin(LED_PORT, LED1_PIN, GPIO_PIN_RESET);

        ((EDF_Task_t*)pvParameters)->deadline += 1000;  // Mise à jour de la deadline

        // Libérer le sémaphore
        xSemaphoreGive(xSemaphore);

        vTaskDelay(pdMS_TO_TICKS(100)); // Délai avant la prochaine exécution
    }
}

void Task2(void *pvParameters) {
    for (;;) {
        // Attendre la permission du scheduler
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        // Prendre le sémaphore
        xSemaphoreTake(xSemaphore, portMAX_DELAY);

        HAL_GPIO_WritePin(LED_PORT, LED2_PIN, GPIO_PIN_SET);
        vTaskDelay(pdMS_TO_TICKS(300));  // Temps d'exécution de la LED2
        HAL_GPIO_WritePin(LED_PORT, LED2_PIN, GPIO_PIN_RESET);

        ((EDF_Task_t*)pvParameters)->deadline += 1500;  // Mise à jour de la deadline

        // Libérer le sémaphore
        xSemaphoreGive(xSemaphore);

        vTaskDelay(pdMS_TO_TICKS(100)); // Délai avant la prochaine exécution
    }
}

void Task3(void *pvParameters) {
    for (;;) {
        // Attendre la permission du scheduler
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        // Prendre le sémaphore
        xSemaphoreTake(xSemaphore, portMAX_DELAY);

        HAL_GPIO_WritePin(LED_PORT, LED3_PIN, GPIO_PIN_SET);
        vTaskDelay(pdMS_TO_TICKS(500));  // Temps d'exécution de la LED3
        HAL_GPIO_WritePin(LED_PORT, LED3_PIN, GPIO_PIN_RESET);

        ((EDF_Task_t*)pvParameters)->deadline += 2000;  // Mise à jour de la deadline

        // Libérer le sémaphore
        xSemaphoreGive(xSemaphore);

        vTaskDelay(pdMS_TO_TICKS(100)); // Délai avant la prochaine exécution
    }
}

// Tâche du scheduler EDF
void EDF_Scheduler_Task(void *pvParameters) {
    for (;;) {
        // Sélectionner la tâche avec la deadline la plus proche
        EDF_Task_t* nextTask = SelectTaskWithEarliestDeadline();

        // Notifier la tâche sélectionnée
        xTaskNotifyGive(nextTask->taskHandle);

        // Attendre avant de réévaluer
        vTaskDelay(pdMS_TO_TICKS(100)); // Fréquence de vérification EDF
    }
}

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

    // Initialiser le tableau EDF
    for (int i = 0; i < 3; i++) {
        edfTasks[i].taskHandle = NULL;
        edfTasks[i].deadline = (i + 1) * 1000;
    }

    // Créer le sémaphore
    xSemaphore = xSemaphoreCreateBinary();
    xSemaphoreGive(xSemaphore);  // Libérer une première fois

    // Créer les tâches
    xTaskCreate(Task1, "Task1", 128, &edfTasks[0], 1, &edfTasks[0].taskHandle);
    xTaskCreate(Task2, "Task2", 128, &edfTasks[1], 1, &edfTasks[1].taskHandle);
    xTaskCreate(Task3, "Task3", 128, &edfTasks[2], 1, &edfTasks[2].taskHandle);

    // Créer la tâche EDF Scheduler
    xTaskCreate(EDF_Scheduler_Task, "EDF_Scheduler", 128, NULL, 2, NULL);

    vTaskStartScheduler();

    while (1);
}

void loop() {
    // Le contrôle est pris en charge par FreeRTOS, donc le loop reste vide.
}