/*
 * Copyright (c) 2026 Martin Javier Klöckner <mklockner@fi.uba.ar>
 *
 * See file `LICENSE` for full details
 */

#include "main.h"

#include "logger.h"
#include "dwt.h"

#include "app.h"
#include "task_buzzer_attribute.h"
#include "task_buzzer_interface.h"

#define G_TASK_BUZZER_TICK_CNT_INI 0ul

#define TICK_INI_MS   0ul
#define BUZZER_FREQ   (1000ul)
#define SHORT_BEEP_MS 35ul
#define LONG_BEEP_MS  500ul

static const task_buzzer_cfg_t task_buzzer_cfg_list[] = {
    {ID_BUZ_A, &htim3, TIM_CHANNEL_1, BUZZER_FREQ, SHORT_BEEP_MS, LONG_BEEP_MS}
};

#define BUZZER_CFG_QTY (sizeof(task_buzzer_cfg_list)/sizeof(task_buzzer_cfg_t))

// Private functions declaration
static void task_buzzer_statechart(void);
static uint16_t task_buzzer_freq_to_pres(uint16_t freq);

// Exported data definition
volatile uint32_t g_task_buzzer_tick_cnt;
task_buzzer_dta_t task_buzzer_dta_list[] = {
    {TICK_INI_MS, ST_BUZ_XX_OFF, EV_BUZ_XX_OFF, false},
};

#define BUZZER_DTA_QTY (sizeof(task_buzzer_dta_list)/sizeof(task_buzzer_dta_t))

// External functions definition
void task_buzzer_init(void *parameters)
{
    uint32_t index;
    const task_buzzer_cfg_t *p_task_buzzer_cfg;
    task_buzzer_dta_t *p_task_buzzer_dta;

    LOGGER_INFO("Initializing `task_buzzer`...");

    g_task_buzzer_tick_cnt = G_TASK_BUZZER_TICK_CNT_INI;

    for (index = 0; BUZZER_DTA_QTY > index; index++)
    {
        p_task_buzzer_cfg = &task_buzzer_cfg_list[index];
        p_task_buzzer_dta = &task_buzzer_dta_list[index];

        p_task_buzzer_dta->tick = TICK_INI_MS;
        p_task_buzzer_dta->state = ST_BUZ_XX_OFF;
        p_task_buzzer_dta->event = EV_BUZ_XX_OFF;
        p_task_buzzer_dta->event_flag = false;

        __HAL_TIM_SET_PRESCALER(p_task_buzzer_cfg->htim,
                task_buzzer_freq_to_pres(BUZZER_FREQ));

        HAL_TIM_PWM_Stop(p_task_buzzer_cfg->htim, p_task_buzzer_cfg->tim_channel);
    }
}

void task_buzzer_update(void *parameters)
{
    bool b_time_update_required = false;

    __asm("CPSID i");
    if (G_TASK_BUZZER_TICK_CNT_INI < g_task_buzzer_tick_cnt)
    {
        g_task_buzzer_tick_cnt--;
        b_time_update_required = true;
    }
    __asm("CPSIE i");

    while (b_time_update_required)
    {
        task_buzzer_statechart();

        __asm("CPSID i");
        if (G_TASK_BUZZER_TICK_CNT_INI < g_task_buzzer_tick_cnt)
        {
            g_task_buzzer_tick_cnt--;
            b_time_update_required = true;
        }
        else
        {
            b_time_update_required = false;
        }
        __asm("CPSIE i");
    }
}

static void task_buzzer_statechart(void)
{
    uint32_t index;
    const task_buzzer_cfg_t *p_task_buzzer_cfg;
    task_buzzer_dta_t *p_task_buzzer_dta;

    for (index = 0; BUZZER_DTA_QTY > index; index++)
    {
        p_task_buzzer_cfg = &task_buzzer_cfg_list[index];
        p_task_buzzer_dta = &task_buzzer_dta_list[index];

        switch (p_task_buzzer_dta->state)
        {
            case ST_BUZ_XX_OFF:
                if (true == p_task_buzzer_dta->event_flag)
                {
                    p_task_buzzer_dta->event_flag = false;
                    switch(p_task_buzzer_dta->event)
                    {
                        case EV_BUZ_XX_SHORT_BEEP:
                            p_task_buzzer_dta->tick = SHORT_BEEP_MS;
                            p_task_buzzer_dta->state = ST_BUZ_XX_ON;
                            HAL_TIM_PWM_Start(p_task_buzzer_cfg->htim,
                                    p_task_buzzer_cfg->tim_channel);
                        break;

                        case EV_BUZ_XX_LONG_BEEP:
                            p_task_buzzer_dta->tick = LONG_BEEP_MS;
                            p_task_buzzer_dta->state = ST_BUZ_XX_ON;
                            HAL_TIM_PWM_Start(p_task_buzzer_cfg->htim,
                                    p_task_buzzer_cfg->tim_channel);
                        break;

                        default: break;
                    }
                }
            break;

            case ST_BUZ_XX_ON:
                if (true == p_task_buzzer_dta->event_flag)
                {
                    p_task_buzzer_dta->event_flag = false;
                    switch(p_task_buzzer_dta->event)
                    {
                        case EV_BUZ_XX_SHORT_BEEP:
                            p_task_buzzer_dta->tick = SHORT_BEEP_MS;
                            p_task_buzzer_dta->state = ST_BUZ_XX_ON;
                            HAL_TIM_PWM_Start(p_task_buzzer_cfg->htim,
                                    p_task_buzzer_cfg->tim_channel);
                        break;

                        case EV_BUZ_XX_LONG_BEEP:
                            p_task_buzzer_dta->tick = LONG_BEEP_MS;
                            p_task_buzzer_dta->state = ST_BUZ_XX_ON;
                            HAL_TIM_PWM_Start(p_task_buzzer_cfg->htim,
                                    p_task_buzzer_cfg->tim_channel);
                        break;

                        case EV_BUZ_XX_OFF:
                        default:
                            p_task_buzzer_dta->tick = TICK_INI_MS;
                            p_task_buzzer_dta->state = ST_BUZ_XX_OFF;
                            HAL_TIM_PWM_Stop(p_task_buzzer_cfg->htim,
                                    p_task_buzzer_cfg->tim_channel);
                        break;
                    }
                }

                if (0 == p_task_buzzer_dta->tick)
                {
                    p_task_buzzer_dta->state = ST_BUZ_XX_OFF;
                    HAL_TIM_PWM_Stop(p_task_buzzer_cfg->htim,
                            p_task_buzzer_cfg->tim_channel);
                }
                else
                {
                    p_task_buzzer_dta->tick--;
                }
            break;

            default:
                p_task_buzzer_dta->tick  = TICK_INI_MS;
                p_task_buzzer_dta->state = ST_BUZ_XX_OFF;
                p_task_buzzer_dta->event = EV_BUZ_XX_OFF;
                p_task_buzzer_dta->event_flag = false;
                HAL_TIM_PWM_Start(p_task_buzzer_cfg->htim,
                        p_task_buzzer_cfg->tim_channel);
            break;
        }
    }
}


// TODO: replace this defines with HAL variables/functions which returns the
// APB1 timer clock and timer 3 auto reload value (if exists)
#define APB1_TIMER_CLOCK 64000000UL
#define TIMER3_ARR       8000UL

static uint16_t task_buzzer_freq_to_pres(uint16_t freq)
{
    if (0 == freq)
    {
        return 0;
    }
    return ((APB1_TIMER_CLOCK / (freq * (TIMER3_ARR - 1))) - 1);
}
