//
// Created by villuton on 01.09.2025.
//

#include "Indication.h"
#include "SystemDelayInterface.h"

/**
 * Инициализация паттерна индикации
 * В случае, если режим предполагает постоянное положения пина индикации
 * Выставляется флаг permanent и большее время для соответствующего состояния
 * @param *ptr tMod
 * @param timeOn uint32_t Время включенного состояния
 * @param timeOff uint32_t Время выключенного состояния
 * @param permanent bool Флаг неизменяемости состояния
 */
static void lIndication_ModInit(tMod *ptr, uint32_t timeOn, uint32_t timeOff, bool permanent) {
    ptr->timer = 0;
    ptr->timeOn = timeOn;
    ptr->timeOff = timeOff;
    ptr->isPermanent = permanent;
    ptr->pinFlag = false;

    if (permanent != false) {
        if (timeOn > timeOff) {
            ptr->pinFlag = true;
        } else {
            ptr->pinFlag = false;
        }
    }
}


/**
 * Функция индикации светодиода, в соответствии с каналом,
 * выставленном на нем режимом и таймером индикации
 * @param *ptr tMod
 * @param iterationMs uint32_t
 * @param *ledPin tGpioPin
 */
static void lIndication_ModSet(tMod *ptr, uint32_t iterationMs, tGpioPin *ledPin) {
    ///Если установлен перманентный режим работы
    if (ptr->isPermanent) {
        GpioPinSet(ledPin, ptr->pinFlag);
        return;
    }
    ///Если таймер истек
    ///Изменить флаг, выставляемый на пин светодиода
    ///Переустановить таймер, в зависимости от предыдущего установленного таймера
    if (ptr->timer < iterationMs) {
        ptr->pinFlag = !ptr->pinFlag;
        ptr->timer = ptr->pinFlag != false ? ptr->timer = iterationMs + ptr->timeOn : iterationMs + ptr->timeOff;
    }
    ///Если вышеперечисленные условия не выполнились, выставить пин светодиода, в соответствии с флагом
    GpioPinSet(ledPin, ptr->pinFlag);
}


/**
 * Функция потока индикации
 * @param *env tIndication
 */
static _Noreturn void Indication_Thread(tIndication *env) {
    for (;;) {
        env->iterationMs = SystemGetMs();
        for (uint8_t i = CHRG_Ch1; i <= INDICATION_CHANEL_NUM; i++) {
            lIndication_ModSet(&env->mod[env->chanel[i]], env->iterationMs, &env->ledPins->tab[i]);
        }
        SystemDelayMs(INDICATION_THREAD_DELAY_MS);
    }
}


/**
 * Инициализация модуля индикации
 * @param *env tIndication
 * @param *ledPins tLedPins
 */
void Indication_Init(tIndication *env,
                     tLedPins *ledPins) {
    env->ledPins = ledPins;
    for (uint8_t i = 0; i <= INDICATION_CHANEL_NUM; i++) {
        env->chanel[i] = MOD_CH_STARTUP;
    }
    lIndication_ModInit(&env->mod[MOD_CH_STARTUP], 500, 100, false);
    lIndication_ModInit(&env->mod[MOD_CH_ERROR], 200, 200, false);
    lIndication_ModInit(&env->mod[MOD_CH_OK], 0, 999, true);
    lIndication_ModInit(&env->mod[MOD_CH_CHARGE], 999, 0, true);
}


/**
 * Запуск потока индикации
 * @param *env tIndication
 */
void Indication_StartThread(tIndication *env) {
    if (!env->thread.id) {
        env->thread.id = osThreadNew((osThreadFunc_t) (Indication_Thread), (void *) (env), &env->thread.attr);
    } else {
        osThreadResume(env->thread.id);
    }
}


/**
 * Изменения режима индикации канала
 * @param *env tIndication
 * @param indicationMod eIndicationMod
 * @param channel eChargerChannels
 */
void Indication_ChanelSetMode(tIndication *env,
                              eIndicationMod indicationMod,
                              eChargerChannels channel) {
    env->chanel[channel] = indicationMod;
}
