/**
 * @author Rémi Heredero (remi@heredero.ch)
 * @version. 1.0.0
 * @date 2023-06-15
 */

#include "blinker.h"


void BLINKER_init(BLINKER* me) {
    me->state = STBL_INIT;
    me->tON = 500;
    me->tOFF = 500;
    me->nBlink = 3;
    me->nBlinkIsOn = false;
    me->remainBlinks = 3;
    me->turnOn.fCallBack = NULL;
    me->turnOff.fCallBack = NULL;
}

blinkerCallBack BLINKER_defineCallBack(fBlinkerCallBack f, void* param) {
    blinkerCallBack c;
    c.fCallBack = f;
    c.param = param;
    return c;
}

void BLINKER_starBehaviour(BLINKER* me) {
    POST(me, &BLINKER_processEvent, evBLinit, 0, 0);
}

void BLINKER_setTurnOn(BLINKER* me, blinkerCallBack callBack) {
    me->turnOn = callBack;
}

void BLINKER_setTurnOff(BLINKER* me, blinkerCallBack callBack) {
    me->turnOff = callBack;
}

void BLINKER_setFinished(BLINKER* me, blinkerCallBack callBack){
    me->finished = callBack;
}

void BLINKER_defineNblink(BLINKER* me, uint8_t n){
    me->nBlink = n;
}

void BLINKER_setTimeOn(BLINKER*me, uint16_t t) {
    me->tON = t;
}

void BLINKER_setTimeOff(BLINKER*me, uint16_t t) {
    me->tOFF = t;
}

void BLINKER_blinkN(BLINKER* me){
    POST(me, &BLINKER_processEvent, evBLblinkN, 0, 0);
}

void BLINKER_blink(BLINKER* me){
    POST(me, &BLINKER_processEvent, evBLblink, 0, 0);
}

bool BLINKER_processEvent(Event* ev) {
    bool processed = false;
    BLINKER* me = (BLINKER*) Event_getTarget(ev);
    BLINKER_STATES oldState = me->state;
    evIDT evid = Event_getId(ev);
    
    switch(me->state) {
        case STBL_INIT:
            if(evid == evBLinit) {
                me->state = STBL_WAIT; 
            }
            break;
            
        case STBL_WAIT:
            me->remainBlinks = me->nBlink;
            
            if(evid == evBLblinkN) {
                me->state = STBL_ON;
                me->nBlinkIsOn = true;
                POST(me, &BLINKER_processEvent, evBLtimer, me->tON, 0);
            }
            
            if(evid==evBLblink) {
                me->state = STBL_ON;
                me->nBlinkIsOn = false;
                POST(me, &BLINKER_processEvent, evBLtimer, me->tON, 0);
            }
            break;
            
        case STBL_ON:
            if(me->nBlinkIsOn) me->remainBlinks--;
            
            if(evid == evBLtimer) {
                me->state = STBL_OFF;
                POST(me, &BLINKER_processEvent, evBLtimer, me->tOFF, 0);
            }
            
            break;
            
        case STBL_OFF:
            
            if(evid == evBLtimer) {
                if(me->remainBlinks == 0) {
                    me->state = STBL_WAIT;
                    if(me->finished.fCallBack != NULL) me->finished.fCallBack(me->finished.param);
                } else {
                    me->state = STBL_ON;
                    POST(me, &BLINKER_processEvent, evBLtimer, me->tON, 0);
                }
            }
            
            break;
    }
    
    if(oldState != me->state) {
        switch(me->state) { // on entry
            case STBL_INIT:
                break;

            case STBL_WAIT:
                break;

            case STBL_ON:
                if(me->turnOn.fCallBack != NULL) me->turnOn.fCallBack(me->turnOn.param);
                break;

            case STBL_OFF:
                if(me->turnOff.fCallBack != NULL) me->turnOff.fCallBack(me->turnOff.param);
                break;
        }
        processed = true;
    }
    return processed;
}

void BLINKER_endBlink(BLINKER* me) {
    me->remainBlinks = 0;
}

void BLINKER_toggle(BLINKER* me){
    switch(me->state) {
        case STBL_WAIT:
            BLINKER_blink(me);
            break;
        case STBL_OFF:
            BLINKER_endBlink(me);
            break;
        case STBL_ON:
            BLINKER_endBlink(me);
            break;
    }
}
