/*******************************************************************************
 Copyright (c) 2016 Lumen Labs (HK) Limited. All Rights Reserved.
*******************************************************************************/

#include <stdio.h>  // sprintf
#include <string.h>
#include "PCA10040.h"
#include "nrf_delay.h"
#include "ble_hci.h"
#include "ble_advertising.h"

#include "LL_Hardware_cfg.h"
#include "LL_Power.h"
#include "LL_Timer.h"
#include "LL_GPIO.h"
#include "LL_Key.h"
#include "LL_PWM.h"
#include "LL_LED_Helmet.h"
#include "LL_Flash.h"
//#include "LL_Pairing.h"
#include "LL_SysMode.h"
#include "LL_Para.h"
#include "LL_Battery.h"
#include "LL_BatteryIndicator.h"
#include "LL_Battery_Charge.h"
#include "LL_MSG.h"
#include "LL_MSG_Handle_Helmet.h"
#include "ble_nus.h"
#ifdef _LL_TIMESLOT
#include "esb_timeslot.h"
#endif

extern uint16_t m_conn_handle;
extern ble_nus_t  m_nus;

extern T_LL_HardwareID gtHwID;

#define LL_BLE_ADV_PAIRING_FLAG 0x0101
extern void LL_BLE_SetAdManu(unsigned short manu);
extern void battery_level_update(void);

static unsigned long sgulGeneralStep;
static unsigned long sgulGeneralCnt;
static unsigned long sgulBeepCnt;

static unsigned long sgulXxxTestPowerOffStep = 0;
static unsigned long sgulXxxTestPowerOffCnt = 0;

#if 0
static unsigned long sgulRedGreenStep = 0;
static unsigned long sgulRedGreenCnt;
void LL_Helmet_RedGreenFlashWhenTestMode_OFF(void) 
{
    LL_LED_OFF(E_LL_LED_CHARGE_GREEN);
    LL_LED_OFF(E_LL_LED_CHARGE_RED);
    sgulRedGreenStep = 0;
}
void LL_Helmet_RedGreenFlashWhenTestMode_ON(void)
{ 
    sgulRedGreenStep = 1; 
}
unsigned long LL_Helmet_RedGreenFlashWhenTestMode_isON(void)
{ 
    if(0==sgulRedGreenStep) return 0; 
    else                    return 1;
}
static void LL_Helmet_RedGreenFlashWhenTestMode(void)
{ 
    switch(sgulRedGreenStep) {
        case  0: break;
        case  1:
            LL_LED_OFF(E_LL_LED_CHARGE_GREEN);
            LL_LED_ON( E_LL_LED_CHARGE_RED);
            // next step
            sgulRedGreenCnt = gulTimerCnt1ms;
            sgulRedGreenStep++; 
            break;
        case  2:
            if( 500 < LL_Timer_Elapsed_ms(sgulRedGreenCnt) ) {
                // next step
                sgulRedGreenCnt = gulTimerCnt1ms;
                sgulRedGreenStep++;                 
            }
            break;
        case   3:
            LL_LED_ON( E_LL_LED_CHARGE_GREEN);
            LL_LED_OFF(E_LL_LED_CHARGE_RED);
            // next step
            sgulRedGreenCnt = gulTimerCnt1ms;
            sgulRedGreenStep++; 
            break;    
        case  4:
            if( 500 < LL_Timer_Elapsed_ms(sgulRedGreenCnt) ) {
                // loop
                sgulRedGreenCnt = gulTimerCnt1ms;
                sgulRedGreenStep = 1;                 
            }
            break;    
    }
}
#endif

T_SysState gtSysState;
T_SysState gtSysState_prev;

#if 1 // Enter pairing-mode instead of power-off, if a Long-Press since power-on.
static unsigned long sgulEnterPairingModeIfLongPress = 0;
#endif

void LL_Helmet_ChangeStateTo_Init()
{
    gtSysState.eOnOFF = SYS_INIT;//SYS_OFF;
    gtSysState.eTurnState = TURNING_NONE;
    gtSysState.eBrakeState = BRAKE_OFF;
    memcpy(&gtSysState_prev, &gtSysState, sizeof(T_SysState));
    // 
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
static inline void LL_Helmet_State_Init(void)
{
    T_LL_KEY_EVT tKeyEvt;
    switch(sgulGeneralStep) {
        case  0:
            // if ON/OFF key pressed: check whether a short or long press.
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (1 == tKeyEvt.evt) ) {
                sgulGeneralCnt = gulTimerCnt1ms;
                sgulGeneralStep++; 
            }
            break;    
        case  1:
            // if ON/OFF key released: ON!
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
                    sgulEnterPairingModeIfLongPress = 0;
                    LL_Helmet_ChangeStateTo_BeepBeforeON(); 
            } else {
                    // if long pressed: enter normal mode, and continue checking whether it's a longer press for pairing-mode.
                    if(500 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { //LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                        sgulEnterPairingModeIfLongPress = 1;
                        LL_Helmet_ChangeStateTo_BeepBeforeON(); 
                    }
            }
            break;    
    }
    
    #ifdef LL_BatteryIndicator__ShowWhenHelmetNotOn
//        if(1 != LL_Helmet_RedGreenFlashWhenTestMode_isON()) {
            LL_Battery_Charging_Indicator();
//        }    
    #endif    
}


//static unsigned long sgaulSparePin[LL_TEST_MODE_PIN_NUM] = LL_TEST_MODE_SPARE_PINS;
static unsigned char sgaucTestModePack[16];
static unsigned long sgulTestModeLedLoopCnt, sgulTestModeLedLoopStep;
void LL_Helmet_ChangeStateTo_PcbTestingMode(void)
{
    gtSysState.eOnOFF = SYS_PCB_TESTING_MODE;
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
    
//  sgulTestModeLedLoopCnt = gulTimerCnt1ms; sgulTestModeLedLoopStep = 0;
    ble_advertising_start(BLE_ADV_MODE_FAST); //timeslot_sd_start();
}
static inline void LL_Helmet_State_PcbTestingMode(void)
{
    unsigned long battery_level;
    switch(sgulGeneralStep) {
        case  0:
            LL_LEDs_OFF(); //LL_LED_ON(E_LL_LED_CHARGE_RED);
            sgulGeneralCnt = gulTimerCnt1ms; sgulGeneralStep++; break;
        case  1: if(1000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { sgulGeneralStep++; } break;
        case  2: 
            LL_LEDs_OFF(); //LL_LED_ON(E_LL_LED_CHARGE_GREEN);
            sgulGeneralCnt = gulTimerCnt1ms; sgulGeneralStep++; break;
        case  3: if(1000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { sgulGeneralStep++; } break;
        case  4:
            LL_LEDs_OFF(); 
            sgulGeneralCnt = gulTimerCnt1ms; sgulGeneralStep++; break;
        case  5:
            battery_level = LL_Battery_Level();
            if(LL_BATTERY_LEVEL_NONE == battery_level) break;
            // pack
            memcpy(sgaucTestModePack, gtHwID.auc, 8); sgaucTestModePack[8] = (unsigned char)battery_level;
            sgulGeneralCnt = gulTimerCnt1ms; sgulGeneralStep++; break;
        case  6: // send to dongle by NUS
            if( NRF_SUCCESS != ble_nus_string_send(&m_nus, (uint8_t *)sgaucTestModePack, 9) ) break;
            sgulGeneralCnt = gulTimerCnt1ms; sgulGeneralStep++; break;
    }
    
    // wait button event to beep:
    T_LL_KEY_EVT tKeyEvt;
    if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) ) {
        if(1 == tKeyEvt.evt) { LL_PWM_ON( E_LL_LED_BUZZER); }
        else                 { LL_PWM_OFF(E_LL_LED_BUZZER); }
    }
    
    // loop the LED stripe pins tested by dongle:
//  E_LL_LED aeLED[8] = { E_LL_LED_TURN_L, E_LL_LED_TURN_R, E_LL_LED_FRONT, E_LL_LED_BRAKE_L, E_LL_LED_BRAKE_R, E_LL_LED_BRAKE_BRIM, E_LL_LED_BACK_R, E_LL_LED_BACK_L };
    E_LL_LED aeLED[9] = { E_LL_LED_TURN_L, E_LL_LED_TURN_R, E_LL_LED_FRONT, E_LL_LED_FrontWhiteL, E_LL_LED_FrontWhiteR, E_LL_LED_BACK_L, E_LL_LED_BACK_R, E_LL_LED_FrontYellowL, E_LL_LED_FrontYellowR };    
    if(100 > LL_Timer_Elapsed_ms(sgulTestModeLedLoopCnt)) { sgulTestModeLedLoopCnt = gulTimerCnt1ms;
        for(int i = 0; i < 9; i++) { LL_LED_OFF(aeLED[i]); } LL_LED_ON(aeLED[sgulTestModeLedLoopStep]);
        sgulTestModeLedLoopStep++; if(9 <= sgulTestModeLedLoopStep){ sgulTestModeLedLoopStep = 0; }
    }
}


void LL_Helmet_ChangeStateTo_BeepBeforeON(void)
{
    gtSysState.eOnOFF = SYS_BEEP_BEFORE_ON;
    sgulBeepCnt = gulTimerCnt1ms;    
    // LED on for both battery-level-measuring and helmet-on-indicator:
    if(1 != gtPara.ulNeedCharge) {
        //
        unsigned char *mode_brightness = gtPara.brightness_individual[gtSysState.eFlashTypeOfBackLight];
        E_LL_LED lights[] = { 
            E_LL_LED_FRONT,
//            E_LL_LED_FrontWhiteL,
//            E_LL_LED_FrontWhiteR,
            E_LL_LED_BACK_L,
            E_LL_LED_BACK_R,
        }; 
        for(int i = 0; i < 5; i++) {
            LL_LED_Brightness(lights[i], mode_brightness[lights[i]]);
            LL_LED_ON(lights[i]);
        }
        //
        LL_LED_Brightness(E_LL_LED_FrontWhiteL, mode_brightness[E_LL_LED_FrontWhiteL]); LL_LED_ON(E_LL_LED_FrontWhiteL);
        LL_LED_Brightness(E_LL_LED_FrontWhiteR, mode_brightness[E_LL_LED_FrontWhiteR]); LL_LED_ON(E_LL_LED_FrontWhiteR);
    }
    LL_BatteryIndicatorOfPowerOn_Start();
    
    sgulGeneralCnt = gulTimerCnt1ms;    
}
static inline void LL_Helmet_State_BeepBeforeON(void)
{
    T_LL_KEY_EVT tKeyEvt;
    
    if(       125 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { if(E_BEEP_MODE_OF_POWER_ON_OFF__BEEP == gtPara.beep_mode_of_power_on_off) { LL_LED_ON(  E_LL_LED_BUZZER ); } }
    else if(  250 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
    else if(  375 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { if(E_BEEP_MODE_OF_POWER_ON_OFF__BEEP == gtPara.beep_mode_of_power_on_off) { LL_LED_ON(  E_LL_LED_BUZZER ); } }
    else if(  500 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
    else                                               { 
//      if(LL_BatteryIndicatorOfPowerOn_IsFinished()) {
        if(true) {
            if(0 == sgulEnterPairingModeIfLongPress) {
                LL_Helmet_ChangeStateTo_ON(); 
            } else {
                if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
                    LL_Helmet_ChangeStateTo_ON();                     
                } else {
                    if(2000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                        #if 1 // Enter pairing-mode instead of power-off, if a Long-Press since power-on.
                        if(0 == sgulEnterPairingModeIfLongPress) {                            
                            LL_Helmet_ChangeStateTo_ON(); 
                        } else { sgulEnterPairingModeIfLongPress = 0;
                            LL_Helmet_ChangeStateTo_Pairing();
                        }
                        #endif
                    }                    
                }

            }
        }
    }
}


/*
void LL_Helmet_ChangeStateTo_BeepBeforeONorPairing(void)
{
    gtSysState.eOnOFF = SYS_BEEP_BEFORE_ON_OR_PAIRING;
    sgulBeepCnt = gulTimerCnt1ms;    
    LL_BatteryIndicatorOfPowerOn_Start();
}
static inline void LL_Helmet_State_BeepBeforeOnOrPairing(void)
{
    T_LL_KEY_EVT tKeyEvt;
    if(       125 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_ON(  E_LL_LED_BUZZER ); }
    else if(  250 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
    else if(  375 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_ON(  E_LL_LED_BUZZER ); }
    else if(  500 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
    else                                               { 
        if(LL_BatteryIndicatorOfPowerOn_IsFinished()) {
            // if released, ON
            // else if longer than 3s, pairing
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
                LL_Helmet_ChangeStateTo_ON(); 
            } else {
                if(3000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                    LL_Helmet_ChangeStateTo_Pairing(); 
                }
            }
        }
    }
}
*/

void LL_Helmet_ChangeStateTo_ON(void)
{
    #if 0 // for buzzer aging
    {
        LL_PWM_Sleep(); LL_PWM_Init(LL_PWM_PERIOD_us(1)); LL_LED_Brightness(E_LL_LED_BUZZER, 50); // 50: any value between 0~100 is ok, just need a wave.
    }
    #endif

    gtSysState.eOnOFF                   = SYS_ON; LL_LEDs_PowerOn_AfterWakeUp();
    gtSysState.ulNoConnectionSinceSysOn = 1;
    gtSysState.eFlashTypeOfBackLight    = gtPara.eMode;//BACK_LIGHT_ALWAYS_ON;
    LL_HelmetActionWhenStateChanged();
//  LL_Battery_LowIndicate();
    LL_BatteryIndicatorOfRuntime_Start();

    LL_BLE_SetAdManu(0);
    battery_level_update();
    ble_advertising_start(BLE_ADV_MODE_FAST); timeslot_sd_start();

    #if 0 // for buzzer aging
    {
        LL_LED_ON(E_LL_LED_BUZZER);
    }
    #endif
        
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
void LL_Helmet_ChangeStateTo_NextMode(void)
{
    gtSysState.eOnOFF = SYS_ON;
    switch(gtSysState.eFlashTypeOfBackLight){
        case BACK_LIGHT_ALWAYS_ON:
            gtSysState.eFlashTypeOfBackLight = BACK_LIGHT_SLOW_FLASH;
            break;
        case BACK_LIGHT_SLOW_FLASH:
            gtSysState.eFlashTypeOfBackLight = BACK_LIGHT_FAST_FLASH;
            break;
        case BACK_LIGHT_FAST_FLASH:
        default:
            gtSysState.eFlashTypeOfBackLight = BACK_LIGHT_ALWAYS_ON;
            break;
    }
    gtPara.eMode = gtSysState.eFlashTypeOfBackLight;  
    gulFlashStoreNeeded = 1;
        
////gtSysState.eTurnState   = TURNING_NONE;
    gtSysState.eBrakeState  = BRAKE_OFF;
    LL_HelmetActionWhenStateChanged();
    
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
void TxCurrentFlashModeToApp__Start(void);
static inline void LL_Helmet_State_ON(void)
{
    T_LL_KEY_EVT tKeyEvt;
    switch(sgulGeneralStep) {
        case  0:
            // if ON/OFF key pressed: check whether a short or long press.
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (1 == tKeyEvt.evt) ) {
                sgulGeneralCnt = gulTimerCnt1ms;
                sgulGeneralStep++; 
            }
            break;    
        case  1:
            // if ON/OFF key released: switch flash mode.
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
                    LL_Helmet_ChangeStateTo_NextMode();
                    TxCurrentFlashModeToApp__Start();//TxCurrentFlashModeToApp();
            } else {
                    // if long pressed: OFF.
                    if(2000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                        LL_Helmet_ChangeStateTo_BeepBeforeOFF(); 
                    }
            }
            break;    
    }
}



void LL_Helmet_ChangeStateTo_BeepBeforePairing(void)
{
    gtSysState.eOnOFF = SYS_BEEP_BEFORE_PAIRING;
    sgulBeepCnt = gulTimerCnt1ms;    
    LL_BatteryIndicatorOfPowerOn_Start();
}
//static inline void LL_Helmet_State_BeepBeforePairing(void)
//{
//    if(       125 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_ON(  E_LL_LED_BUZZER ); }
//    else if(  250 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
//    else if(  375 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_ON(  E_LL_LED_BUZZER ); }
//    else if(  500 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
//    else                                               { 
//        if(LL_BatteryIndicatorOfPowerOn_IsFinished()) {
//            LL_Helmet_ChangeStateTo_Pairing(); 
//        }
//    }
//}



void LL_Helmet_ChangeStateTo_Pairing(void)
{
    gtSysState.eOnOFF = SYS_PAIRING;//SYS_ON;
    LL_LEDs_PowerOn_AfterWakeUp();
    gtSysState.ulNoConnectionSinceSysOn = 1;
    gtSysState.eTurnState            = TURNING_NONE;
    gtSysState.eBrakeState           = BRAKE_OFF;
    LL_HelmetActionWhenStateChanged();
    {// front and orange flashing instead of breathing
        E_LL_LED lights[] = { 
            E_LL_LED_FRONT,
//            E_LL_LED_FrontWhiteL,
//            E_LL_LED_FrontWhiteR,
            E_LL_LED_TURN_L,
            E_LL_LED_TURN_R,
            E_LL_LED_FrontYellowL,
            E_LL_LED_FrontYellowR,
        }; 
        unsigned long brightness[] = { 
            20, 
//            SHIELD_LIGHTS_FIXED_BRIGHTNESS, SHIELD_LIGHTS_FIXED_BRIGHTNESS, 
            100, 100, 100, 100 
        };
        unsigned long flashing_ms_on    = 500;
        unsigned long flashing_ms_off   = 500;
        for(int i = 0; i < 7; i++) {
            LL_LED_Brightness(lights[i], brightness[i]); LL_LED_Flashing(lights[i],  flashing_ms_on, flashing_ms_off);
        }
        if(1 == gtPara.ulBrakeFunction) { 
            LL_LED_Brightness(E_LL_LED_BACK_L, 20); LL_LED_ON(E_LL_LED_BACK_L);
            LL_LED_Brightness(E_LL_LED_BACK_R, 20); LL_LED_ON(E_LL_LED_BACK_R);
        } else {
            LL_LED_OFF(E_LL_LED_BACK_L);
            LL_LED_OFF(E_LL_LED_BACK_R);
        }
    }
//  LL_Battery_LowIndicate();
    LL_BatteryIndicatorOfRuntime_Start();
    
    LL_BLE_SetAdManu(LL_BLE_ADV_PAIRING_FLAG);
    battery_level_update();
    ble_advertising_start(BLE_ADV_MODE_FAST); timeslot_sd_start();

//    if(0 == gtPara.ulBrakeFunction) { LL_PWM_OFF(E_LL_LED_BRAKE_L);
//                                      LL_PWM_OFF(E_LL_LED_BRAKE_R);    }
//    else                            { LL_PWM_DutySet(E_LL_LED_BRAKE_L, 100); LL_PWM_ON( E_LL_LED_BRAKE_L);
//                                      LL_PWM_DutySet(E_LL_LED_BRAKE_R, 100); LL_PWM_ON( E_LL_LED_BRAKE_R); }
    
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
//static unsigned long sgulBreathingBrightness = 0;
//static unsigned long sgulBreathingCnt = 0;
static unsigned long sgulBrakeOnOffClickCnt = 0;
static unsigned long sgulBrakeOnOffTimeCnt = 0;
//void IndicatorOfBrakeFuncOnOff__Start(void);
static inline void LL_Helmet_State_Pairing(void)
{
#if 0    
    // breathing
    if( 8 <= LL_Timer_Elapsed_ms(sgulBreathingCnt) ) { sgulBreathingCnt = gulTimerCnt1ms;
        sgulBreathingBrightness++; if(200<sgulBreathingBrightness){sgulBreathingBrightness=0;}
        unsigned long brightness = sgulBreathingBrightness<=100 ? sgulBreathingBrightness : 200-sgulBreathingBrightness;
        LL_LED_Brightness(E_LL_LED_FRONT, brightness);
        // rear lights also breathing
        LL_LED_Brightness(E_LL_LED_BACK_L, brightness);
        LL_LED_Brightness(E_LL_LED_BACK_R, brightness);
    }    
#endif
    
    T_LL_KEY_EVT tKeyEvt;
    switch(sgulGeneralStep) {
        case  0:
            // if ON/OFF key pressed: check whether a short or long press.
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (1 == tKeyEvt.evt) ) {
                sgulGeneralCnt = gulTimerCnt1ms;
                sgulGeneralStep++; 
            }
            break;    
        case  1:
            // if ON/OFF key released: exit pairing mode.
            if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
//                  LL_Helmet_ChangeStateTo_ON();
                    // 5 clicks to switch brake on/off:
                    sgulBrakeOnOffClickCnt++;
                    if(1 == sgulBrakeOnOffClickCnt) { 
                        // first click: start timing.
                        sgulBrakeOnOffTimeCnt = gulTimerCnt1ms;  
                    } else {
                        // following clicks: re-counting if timeout
                        if(5000 < LL_Timer_Elapsed_ms(sgulBrakeOnOffTimeCnt)) {
                            sgulBrakeOnOffClickCnt = 0;
                        }
                    }
                    if(5 <= sgulBrakeOnOffClickCnt) { sgulBrakeOnOffClickCnt = 0;
                        if(0 == gtPara.ulBrakeFunction) { 
                            gtPara.ulBrakeFunction = 1; gulFlashStoreNeeded = 1; 
                            LL_LED_Brightness(E_LL_LED_BACK_L, 10); LL_LED_ON(E_LL_LED_BACK_L);
                            LL_LED_Brightness(E_LL_LED_BACK_R, 10); LL_LED_ON(E_LL_LED_BACK_R);                            
                        } else { 
                            gtPara.ulBrakeFunction = 0; gulFlashStoreNeeded = 1;
                            LL_LED_OFF(E_LL_LED_BACK_L);
                            LL_LED_OFF(E_LL_LED_BACK_R);                            
                        }
                        //IndicatorOfBrakeFuncOnOff__Start();
                    }
                    // BACK TO STEP 0
                    sgulGeneralStep = 0;
            } else {
                    // if long pressed: OFF.
                    if(2000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                        LL_Helmet_ChangeStateTo_BeepBeforeOFF(); 
                    }
            }
            break;    
    }    
}



void LL_Helmet_ChangeStateTo_BeepBeforeOFF(void)
{
    gtSysState.eOnOFF = SYS_BEEP_BEFORE_OFF;
    sgulBeepCnt = gulTimerCnt1ms; //LL_LED_ON(E_LL_LED_BUZZER);
    //LL_LEDs_PowerOff_BeforeSleep();
    LL_BatteryIndicatorOfPowerOn_Start(); // also need Low Battery Indicator
    LL_BeepForTurning__OFF();
}
static inline void LL_Helmet_State_BeepBeforeOFF(void)
{
    if(       125 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { if(E_BEEP_MODE_OF_POWER_ON_OFF__BEEP == gtPara.beep_mode_of_power_on_off) { LL_LED_ON(  E_LL_LED_BUZZER ); } }
    else if(  250 > LL_Timer_Elapsed_ms(sgulBeepCnt) ) { LL_LED_OFF( E_LL_LED_BUZZER ); }
    else { 
//      if(LL_BatteryIndicatorOfPowerOn_IsFinished()) { // if the Low Battery Indicator done 
        if(true) {
            LL_LEDs_PowerOff_BeforeSleep(); LL_Helmet_ChangeStateTo_OFF(); 
        } else { /* else, need wait for the Low Battery Indicator done */ }
    }
}


void LL_Helmet_ChangeStateTo_OFF(void)
{
    gtSysState.eOnOFF = SYS_OFF;
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
static inline void LL_Helmet_State_OFF(void)
{
    char string[16];
    unsigned long ulLen;
    switch(sgulGeneralStep) {
        case  0:
            sprintf(string, "DSC");
            ble_nus_string_send(&m_nus, (uint8_t *)string, 3);
            sgulGeneralCnt = gulTimerCnt1ms;    
            sgulGeneralStep++;
            break;
        case  1: if(500 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { sgulGeneralStep++; }
            break;
        case  2:
            sprintf(string, "BL:%u\0", LL_Battery_Level()); ulLen = strlen(string);
            ble_nus_string_send(&m_nus, (uint8_t *)string, ulLen);
            sgulGeneralCnt = gulTimerCnt1ms;    
            sgulGeneralStep++;
            break;
        case  3: if(500 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { sgulGeneralStep++; }
            break;
        case  4: 
            if(BLE_CONN_HANDLE_INVALID != m_conn_handle) { 
                sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            }
            sgulGeneralCnt = gulTimerCnt1ms;    
            sgulGeneralStep++;
            break;
        case  5: if(2000 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) { sgulGeneralStep++; }
            break;
        case  6:
            if( 1 == LL_GPIO_InputRead(gatKeyCfg[LL_KEY_NUM_ONOFF].port, gatKeyCfg[LL_KEY_NUM_ONOFF].pin) ) { 
                #ifdef LL_BatteryIndicator__ShowWhenHelmetNotOn
                    if(LL_Battery_Charge__isChargingCablePlugged()) {
                        LL_LEDs_OFF(); //sgulBatteryLevel_prev = LL_BATTERY_LEVEL_NONE; LL_LED_Panel_SetAnimation(LL_LED_PANEL_ANIMATION_OFF, E_LL_ANIMATION__RE_TRIGGER_TYPE__NOT_IF_SAME); // trigger the charging module to display the battery level
                        LL_Helmet_ChangeStateTo_Init();
                    } else {
                        // do a real power-off if the "sys-off long press" released.
                        LL_Power_BeforeSleep(); 
                        LL_Power_Sleep();                         
                    }
                #else
                    // do a real power-off if the "sys-off long press" released.
                    LL_Power_BeforeSleep(); 
                    LL_Power_Sleep(); 
                #endif
            } else {
                // do nothing, just wait key released.
            }
            break;
    }
}



// LL_BeepForTurning
unsigned long sgulBeepWhenTurning_Cnt = 0;
static inline void LL_BeepForTurning(void) { if(0 == sgulBeepWhenTurning_Cnt) return;
    unsigned long ulDelta = LL_Timer_Elapsed_ms(sgulBeepWhenTurning_Cnt);
    if((LL_LED_TURN_FLASH_ON>>1) > ulDelta) { LL_LED_ON(E_LL_LED_BUZZER); } // >>1 : to make beep shorter
    else if((LL_LED_TURN_FLASH_ON + LL_LED_TURN_FLASH_OFF) > ulDelta) { LL_LED_OFF(E_LL_LED_BUZZER); }
    else { LL_Timer_CntStop(sgulBeepWhenTurning_Cnt); }
}
void LL_BeepForTurning__OFF(void) {
    // module LL_LED
    LL_LED_OFF(E_LL_LED_BUZZER);
    // module LL_BeepForTurning
    LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
}
//static inline void LL_BeepForTurning__ON_forLR(void) {
//    unsigned long ulON, ulOFF;
//    switch(gtPara.eBeepMode) {
//        case BEEP_WHEN_TURNING_ONOFF:
//            // module LL_LED
//            LL_LED_OFF(E_LL_LED_BUZZER);
//            // module LL_BeepForTurning
//            LL_Timer_CntStart(sgulBeepWhenTurning_Cnt);
//        break;
//        case BEEP_EVERY_1_TURNING_FLASH:
//            // module LL_LED
//            ulON  = LL_LED_BEEP_FLASH_ON;//LL_LED_TURN_FLASH_ON>>1; // >>1 : to make beep shorter
//            ulOFF = LL_LED_BEEP_FLASH_OFF;//LL_LED_TURN_FLASH_ON + LL_LED_TURN_FLASH_OFF - ulON;
//            LL_LED_Flashing(E_LL_LED_BUZZER, ulON, ulOFF);
//            // module LL_BeepForTurning
//            LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
//        break;
//        case BEEP_EVERY_4_TURNING_FLASH:
//            // module LL_LED
//            ulON  = LL_LED_BEEP_FLASH_ON;//LL_LED_TURN_FLASH_ON>>1; // >>1 : to make beep shorter
//            ulOFF = LL_LED_BEEP_FLASH_OFF;//((LL_LED_TURN_FLASH_ON + LL_LED_TURN_FLASH_OFF)<<2)  - ulON; // <<2: *4
//            LL_LED_Flashing(E_LL_LED_BUZZER, ulON, ulOFF);
//            // module LL_BeepForTurning
//            LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
//        break;
//        case BEEP_EVERY_8_TURNING_FLASH:
//            // module LL_LED
//            ulON  = LL_LED_BEEP_FLASH_ON;//LL_LED_TURN_FLASH_ON>>1; // >>1 : to make beep shorter
//            ulOFF = LL_LED_BEEP_FLASH_OFF;//((LL_LED_TURN_FLASH_ON + LL_LED_TURN_FLASH_OFF)<<3)  - ulON; // <<3: *8
//            LL_LED_Flashing(E_LL_LED_BUZZER, ulON, ulOFF);
//            // module LL_BeepForTurning
//            LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
//        break;
//        case BEEP_NONE:
//        default:
//            // module LL_LED
//            LL_LED_OFF(E_LL_LED_BUZZER);
//            // module LL_BeepForTurning
//            LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
//        break;
//    }
//}
//static inline void LL_BeepForTurning__ON_forNONE(void) {
//    switch(gtPara.eBeepMode) {
//        case BEEP_WHEN_TURNING_ONOFF:
//            // module LL_LED
//            LL_LED_OFF(E_LL_LED_BUZZER);
//            // module LL_BeepForTurning
//            LL_Timer_CntStart(sgulBeepWhenTurning_Cnt);
//        break;
//        default:
//            // module LL_LED
//            LL_LED_OFF(E_LL_LED_BUZZER);
//            // module LL_BeepForTurning
//            LL_Timer_CntStop(sgulBeepWhenTurning_Cnt);
//        break;
//    }
//}
void LL_BeepForTurning__ON(void) {
//        switch(gtSysState.eTurnState) {
//            case TURNING_L:     LL_BeepForTurning__ON_forLR();
//            break;
//            case TURNING_R:     LL_BeepForTurning__ON_forLR();
//            break;
//            case TURNING_NONE:  LL_BeepForTurning__ON_forNONE();
//            break;
//        }
}



static unsigned long ulCntTurnOffBrake = 0;
static inline void LL_Helmet_TurnOffBrakeLightAuto(void)
{
    if(BRAKE_OFF == gtSysState.eBrakeState) {
        ulCntTurnOffBrake = gulTimerCnt1ms; 
    } else if(2*1000 < LL_Timer_Elapsed_ms(ulCntTurnOffBrake)) { 
        gtSysState.eBrakeState = BRAKE_OFF;
        LL_HelmetActionWhenStateChanged();
    }
}
void LL_Helmet_TurnOffBrakeLightAuto_ReclockWhenReceiveNewBrake(void) {
    ulCntTurnOffBrake = gulTimerCnt1ms;     
}

static unsigned long ulCntTurnOffTurning = 0;
static inline void LL_Helmet_TurnOffTurningLightAuto(void)
{
    if(TURNING_NONE == gtSysState.eTurnState) {
        ulCntTurnOffTurning = gulTimerCnt1ms; 
    } else if(2*60*1000 < LL_Timer_Elapsed_ms(ulCntTurnOffTurning)) { 
        gtSysState.eTurnState = TURNING_NONE;
        LL_HelmetActionWhenStateChanged();
    }    
}

unsigned long gulCmdTestingMode = 0;
static unsigned long sgulLEDloop = 0;
static unsigned char sgaucFinalTestPack[20];
void LL_Helmet_ChangeStateTo_ProductionTestingMode(void)
{
    gtSysState.eOnOFF = SYS_PRODUCTION_TESTING_MODE;
    ble_advertising_start(BLE_ADV_MODE_FAST); timeslot_sd_start();
//    LL_Helmet_RedGreenFlashWhenTestMode_ON();
    gulCmdTestingMode = 0;
    // general
    sgulGeneralStep = 0;
    sgulGeneralCnt = gulTimerCnt1ms;    
}
static inline void LL_Helmet_State_ProductionTestingMode(void)
{
    T_LL_KEY_EVT tKeyEvt;
    unsigned long battery_level;
    switch(sgulGeneralStep) {
        case  0:
            LL_LEDs_OFF();
            // turn off the brake
            gtPara.ulBrakeFunction = 0; gulFlashStoreNeeded = 1;
            // next step:
            sgulGeneralCnt = gulTimerCnt1ms;
            sgulGeneralStep++; 
            break;
        case 1:
            if( 500 < LL_Timer_Elapsed_ms(sgulGeneralCnt) ) {
                // next step:
                sgulGeneralCnt = gulTimerCnt1ms;
                sgulGeneralStep++; 
            }
            break;
        case 2:
            battery_level = LL_Battery_Level();
            if(LL_BATTERY_LEVEL_NONE == battery_level) break;
            // pack
            memcpy(&(sgaucFinalTestPack[0]), gtHwID.auc, 8);
            sgaucFinalTestPack[8] = LL_VER_H;
            sgaucFinalTestPack[9] = LL_VER_L;
            memcpy(&(sgaucFinalTestPack[10]), gtPara.tRemote.HardwareID[gtPara.tRemote.ulIng].auc, 8);
            sgaucFinalTestPack[16] = (unsigned char)battery_level;
            // next step:
            sgulGeneralCnt = gulTimerCnt1ms;
            sgulGeneralStep++; 
            break;        
        case 3:
            if( NRF_SUCCESS != ble_nus_string_send(&m_nus, (uint8_t *)sgaucFinalTestPack, 17) ) break;
            // next step:
            sgulGeneralCnt = gulTimerCnt1ms;
            sgulGeneralStep++; 
            break;
        case 4:
            // next step:
            sgulGeneralCnt = gulTimerCnt1ms;
            sgulGeneralStep++; 
            break;
    }
    
//    LL_Helmet_RedGreenFlashWhenTestMode();
    
    // power off & LED-loop:
    if(0 == sgulXxxTestPowerOffStep) {
        // if ON/OFF key pressed: check whether a short or long press.
        if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (1 == tKeyEvt.evt) ) {
            sgulXxxTestPowerOffCnt = gulTimerCnt1ms;
            sgulXxxTestPowerOffStep++; 
        }
    } else if(1 == sgulXxxTestPowerOffStep) {
        // if ON/OFF key released: switch flash mode.
        if( LL_Key_EvtFetch(&tKeyEvt) && (LL_KEY_NUM_ONOFF == tKeyEvt.N) && (0 == tKeyEvt.evt) ) {
                sgulXxxTestPowerOffStep = 0;
                // loop-LED£º
                E_LL_LED lights[] = { 
                    E_LL_LED_TURN_L,
                    E_LL_LED_TURN_R,
                    E_LL_LED_FrontWhiteL,
                    E_LL_LED_FrontWhiteR,
                    E_LL_LED_FRONT,
                    E_LL_LED_FrontYellowR,
                    E_LL_LED_FrontYellowL,
                    E_LL_LED_BACK_R,
                    E_LL_LED_BACK_L,
                }; if(9 <= sgulLEDloop) { sgulLEDloop=0; } LL_LEDs_OFF(); LL_PWM_DutySet(lights[sgulLEDloop], 100); LL_PWM_ON(lights[sgulLEDloop]); sgulLEDloop++;
        } else {
                // if long pressed: OFF.
                if(2000 < LL_Timer_Elapsed_ms(sgulXxxTestPowerOffCnt) ) { LL_Key_IgnoreNextRelease(LL_KEY_NUM_ONOFF); 
                    sgulXxxTestPowerOffStep = 0;
                    LL_Helmet_ChangeStateTo_BeepBeforeOFF(); 
                }
        }
    }    
}



void LL_Helmet_Mainloop(void)
{
    switch(gtSysState.eOnOFF) {
        case SYS_INIT                       : LL_Helmet_State_Init();                   break;
        case SYS_PCB_TESTING_MODE           : LL_Helmet_State_PcbTestingMode();         break;
        case SYS_PRODUCTION_TESTING_MODE    : LL_Helmet_State_ProductionTestingMode();  break;
        case SYS_BEEP_BEFORE_ON             : LL_Helmet_State_BeepBeforeON();           break;
        case SYS_ON                         : LL_Helmet_State_ON();                     break;
//        case SYS_BEEP_BEFORE_PAIRING        : LL_Helmet_State_BeepBeforePairing();      break;
        case SYS_PAIRING                    : LL_Helmet_State_Pairing();                break;
        case SYS_BEEP_BEFORE_OFF            : LL_Helmet_State_BeepBeforeOFF();          break;
        case SYS_OFF                        : LL_Helmet_State_OFF();                    break;
    }
    
    LL_BeepForTurning();
    
    LL_Helmet_TurnOffBrakeLightAuto();   // turn off the brake LED after 2s:    
    LL_Helmet_TurnOffTurningLightAuto(); // turn off the turning LED after 2min:
}
