/**
 *  Author(s)...: Pashgan    http://ChipEnable.Ru
 *  Target(s)...: любой микроконтроллер mega
 *  Compiler....: IAR 5.11A
 *  Description.: Заготовка для событийной системы на таблицах
 *  Data........: 30.09.12
*/

#include "event-system.h"

static void EmptyFunc(void);

/* ring buffer */
static volatile es_event_t cycleBuf[SIZE_BUF];
static volatile uint8_t tailBuf = 0;
static volatile uint8_t headBuf = 0;
static volatile uint8_t countBuf = 0;

static volatile es_state_t _State;

typedef struct {
  es_state_t state;
  es_event_t event;
  es_state_t nextState;
  void (*pStateFunc1)(void);
  void (*pStateFunc2)(void);
} table_state_t;

/**
  * Sate Table - heart of state machine
  */
const table_state_t stateTable[] = {
  /* STATE    EVENT   NEXT STATE  STATE_FUNC1 STATE_FUNC2 */
  /* show date, time, ... */
  // 'Select' Button pressed
  {stShowTime,     evBTN1Pressed, stShowWDay,     showWD,       NULL},
  {stShowWDay,     evBTN1Pressed, stShowDM,       showDayMon,   NULL},
  {stShowDM,       evBTN1Pressed, stShowYear,     showYear,     NULL},
  {stShowYear,     evBTN1Pressed, stShowDNhours,  showDNhour,   NULL},
  {stShowDNhours,  evBTN1Pressed, stShowDNbright, showDNbright, NULL},
  {stShowDNbright, evBTN1Pressed, stShowDNmode,   showDNmode,   NULL},
  {stShowDNmode,   evBTN1Pressed, stShowDNcolour, showDNcolour, NULL},
  {stShowDNcolour, evBTN1Pressed, stShowTime,     showTime,     NULL},
  // '+' Button pressed
  {stShowTime, evBTN2Pressed, stShowMMSS, showMMSS,   NULL},
  {stShowMMSS, evBTN2Pressed, stShowTime, showTime,   NULL},
/*
  // Top Buton pressed
  {stShowTime,        evBTN4Pressed, stShowTemperature, showTemperature, NULL},
  {stShowTemperature, evBTN4Pressed, stShowHumidity,    showHumidity,    NULL},
  {stShowHumidity,    evBTN4Pressed, stShowPressure,    showPressure,    NULL},
  {stShowPressure,    evBTN4Pressed, stShowTime,        showTime,        NULL},
*/
  /* display wdt events */
  {stShowWDay,        evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDM,          evDisplayWDT, stShowTime, showTime, NULL},
  {stShowYear,        evDisplayWDT, stShowTime, showTime, NULL},
  {stShowHumidity,    evDisplayWDT, stShowTime, showTime, NULL},
  {stShowTemperature, evDisplayWDT, stShowTime, showTime, NULL},
  {stShowPressure,    evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDNhours,     evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDNbright,    evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDNmode,      evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDNcolour,    evDisplayWDT, stShowTime, showTime, NULL},

  /* refresh output */
  {stShowTime, evNewSecond,  stNoChange, showTime,   NULL},
  {stShowMMSS, evNewSecond,  stNoChange, showMMSS,   NULL},

  /* set time */
  {stShowTime, evBTN1Holded,  stSetHH,    setTimeBegin, setHHBegin},
  {stSetHH,    evBTN1Holded,  stShowTime, setTimeEnd,   showTime},
  {stSetMM,    evBTN1Holded,  stShowTime, setTimeEnd,   showTime},

  {stSetHH,    evBTN1Pressed, stSetMM,    setMMBegin,   NULL},
  {stSetMM,    evBTN1Pressed, stSetHH,    setHHBegin,   NULL},

  {stSetHH,    evBTN2Pressed, stNoChange, setHHInc,     setTimeShow},
  {stSetHH,    evBTN3Pressed, stNoChange, setHHDec,     setTimeShow},
  {stSetMM,    evBTN2Pressed, stNoChange, setMMInc,     setTimeShow},
  {stSetMM,    evBTN3Pressed, stNoChange, setMMDec,     setTimeShow},

  {stSetHH,    evDisplayWDT,  stShowTime, showTime,     NULL},
  {stSetMM,    evDisplayWDT,  stShowTime, showTime,     NULL},

  /* set calendar */
  // begin
  {stShowWDay, evBTN1Holded, stSetWD,   setDateBegin, setWDBegin},
  {stShowDM,   evBTN1Holded, stSetMD,   setDateBegin, setMDBegin},
  {stShowYear, evBTN1Holded, stSetYear, setDateBegin, setYearBegin},
  // end
  {stSetWD,   evBTN1Holded, stShowWDay, setDateEnd, showWD},
  {stSetMD,   evBTN1Holded, stShowDM,   setDateEnd, showDayMon},
  {stSetMon,  evBTN1Holded, stShowDM,   setDateEnd, showDayMon},
  {stSetYear, evBTN1Holded, stShowYear, setDateEnd, showYear},
  // next
  {stSetWD,   evBTN1Pressed, stSetMD,   setMDBegin,    NULL},
  {stSetMD,   evBTN1Pressed, stSetMon,  setMonthBegin, NULL},
  {stSetMon,  evBTN1Pressed, stSetYear, setYearBegin,  NULL},
  {stSetYear, evBTN1Pressed, stSetWD,   setWDBegin,    NULL},
  // change
  {stSetWD,   evBTN2Pressed, stNoChange, setIncWDay,  setWDShow},
  {stSetWD,   evBTN3Pressed, stNoChange, setDecWDay,  setWDShow},
  {stSetMD,   evBTN2Pressed, stNoChange, setIncMDay,  setDMShow},
  {stSetMD,   evBTN3Pressed, stNoChange, setDecMDay,  setDMShow},
  {stSetMon,  evBTN2Pressed, stNoChange, setIncMonth, setDMShow},
  {stSetMon,  evBTN3Pressed, stNoChange, setDecMonth, setDMShow},
  {stSetYear, evBTN2Pressed, stNoChange, setIncYear,  setYearShow},
  {stSetYear, evBTN3Pressed, stNoChange, setDecYear,  setYearShow},
  // WDT
  {stSetWD,   evDisplayWDT, stShowTime, showTime, NULL},
  {stSetMD,   evDisplayWDT, stShowTime, showTime, NULL},
  {stSetMon,  evDisplayWDT, stShowTime, showTime, NULL},
  {stSetYear, evDisplayWDT, stShowTime, showTime, NULL},

  /* set lighting */
  // begin
  {stShowDNhours,  evBTN1Holded, stSetDayHour,   setDNbegin, setDayHourBegin},
  {stShowDNbright, evBTN1Holded, stSetDayBright, setDNbegin, setDayBrightBegin},
  {stShowDNmode,   evBTN1Holded, stSetDayMode,   setDNbegin, setDayModeBegin},
  {stShowDNcolour, evBTN1Holded, stSetDayColour, setDNbegin, setDayColourBegin},
  // end
  {stSetDayHour,     evBTN1Holded, stShowDNhours,  setDNend, showDNhour},
  {stSetNightHour,   evBTN1Holded, stShowDNhours,  setDNend, showDNhour},
  {stSetDayBright,   evBTN1Holded, stShowDNbright, setDNend, showDNbright},
  {stSetNightBright, evBTN1Holded, stShowDNbright, setDNend, showDNbright},
  {stSetDayMode,     evBTN1Holded, stShowDNmode,   setDNend, showDNmode},
  {stSetNightMode,   evBTN1Holded, stShowDNmode,   setDNend, showDNmode},
  {stSetDayColour,   evBTN1Holded, stShowDNcolour, setDNend, showDNcolour},
  {stSetNightColour, evBTN1Holded, stShowDNcolour, setDNend, showDNcolour},
  // next
  {stSetDayHour,     evBTN1Pressed, stSetNightHour,   setNightHourBegin,   NULL},
  {stSetNightHour,   evBTN1Pressed, stSetDayBright,   setDayBrightBegin,   NULL},
  {stSetDayBright,   evBTN1Pressed, stSetNightBright, setNightBrightBegin, NULL},
  {stSetNightBright, evBTN1Pressed, stSetDayMode,     setDayModeBegin,     NULL},
  {stSetDayMode,     evBTN1Pressed, stSetNightMode,   setNightModeBegin,   NULL},
  {stSetNightMode,   evBTN1Pressed, stSetDayColour,   setDayColourBegin,   NULL},
  {stSetDayColour,   evBTN1Pressed, stSetNightColour, setNightColourBegin, NULL},
  {stSetNightColour, evBTN1Pressed, stSetDayHour,     setDayHourBegin,     NULL},
  // change
  {stSetDayHour,     evBTN2Pressed, stNoChange, setIncDayHour,     showDNhour},
  {stSetDayHour,     evBTN3Pressed, stNoChange, setDecDayHour,     showDNhour},
  {stSetNightHour,   evBTN2Pressed, stNoChange, setIncNightHour,   showDNhour},
  {stSetNightHour,   evBTN3Pressed, stNoChange, setDecNightHour,   showDNhour},
  {stSetDayBright,   evBTN2Pressed, stNoChange, setIncDayBright,   showDNbright},
  {stSetDayBright,   evBTN3Pressed, stNoChange, setDecDayBright,   showDNbright},
  {stSetNightBright, evBTN2Pressed, stNoChange, setIncNightBright, showDNbright},
  {stSetNightBright, evBTN3Pressed, stNoChange, setDecNightBright, showDNbright},
  {stSetDayMode,     evBTN2Pressed, stNoChange, setIncDayMode,     showDNmode},
  {stSetDayMode,     evBTN3Pressed, stNoChange, setDecDayMode,     showDNmode},
  {stSetNightMode,   evBTN2Pressed, stNoChange, setIncNightMode,   showDNmode},
  {stSetNightMode,   evBTN3Pressed, stNoChange, setDecNightMode,   showDNmode},
  {stSetDayColour,   evBTN2Pressed, stNoChange, setIncDayColour,   showDNcolour},
  {stSetDayColour,   evBTN3Pressed, stNoChange, setDecDayColour,   showDNcolour},
  {stSetNightColour, evBTN2Pressed, stNoChange, setIncNightColour, showDNcolour},
  {stSetNightColour, evBTN3Pressed, stNoChange, setDecNightColour, showDNcolour},
  // WDT
  {stSetDayHour,     evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetNightHour,   evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetDayBright,   evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetNightBright, evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetDayMode,     evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetNightMode,   evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetDayColour,   evDisplayWDT, stShowTime, setDNbreak, showTime},
  {stSetNightColour, evDisplayWDT, stShowTime, setDNbreak, showTime},

  /* mandatory empty table row */
  {stNoChange, eventNull, stNoChange, EmptyFunc, EmptyFunc}
};

/**
  * @brief  Take event.
  * @param  None
  * @retval Event code
  */
es_event_t ES_GetEvent(void)
{
  es_event_t event;

  if (countBuf > 0) {
    event = cycleBuf[headBuf];
    countBuf --;
    headBuf = (headBuf + 1) & (SIZE_BUF - 1);
    return event;
  }

  return eventNull;
}

/**
  * @brief  Place event.
  * @param  Event
  * @retval None
  */
void ES_PlaceEvent(es_event_t event)
{
  if (countBuf < SIZE_BUF) {
    cycleBuf[tailBuf] = event;
    tailBuf = (tailBuf + 1) & (SIZE_BUF - 1);
    countBuf ++;
    /* сигнализация переполнения буфера событий */
  } else {
      // tube power off
      TIM1->CCER &= ~(TIM_CCER_CC1E);
      TIM3->CCER &= ~(TIM_CCER_CC4E);
      TIM3->CCER &= ~(TIM_CCER_CC3E);
      TIM3->CCER &= ~(TIM_CCER_CC2E);
      TIM3->CCER &= ~(TIM_CCER_CC1E);
      GPIOA->BSRR = 0x10;
      // color - pink
      TIM1->CCR2 = 0xff;
      TIM1->CCR3 = 0x14;
      TIM1->CCR4 = 0x93;
      while(1);
  }
}

/**
  * @brief  Initialize event system.
  * @param  Start Event
  * @retval None
  */
void ES_Init(es_state_t init_state)
{
  tailBuf = 0;
  headBuf = 0;
  countBuf = 0;
  _State = init_state;
}

/**
  * @brief  Fake function.
  * @param  None
  * @retval None
  */
static void EmptyFunc(void)
{
}

/**
  * @brief  Dispatcher of event system.
  * @param  Event
  * @retval None
  */
void ES_Dispatch(es_event_t event)
{
  uint8_t i;
  void (*pStateFunc1)(void);
  void (*pStateFunc2)(void);

  pStateFunc1 = NULL;
  pStateFunc2 = NULL;

  /* get next state */
  for (i=0; stateTable[i].state; i++) {
    /* if the current state and the received event match */
    if (stateTable[i].state == _State && stateTable[i].event == event) {
      /* change current state if needed */
      if (stateTable[i].nextState != stNoChange) {
        _State = stateTable[i].nextState;
      }
      /* get functions */
      pStateFunc1 = stateTable[i].pStateFunc1;
      pStateFunc2 = stateTable[i].pStateFunc2;
      break;
    }
  }

  if (pStateFunc1) {
    pStateFunc1();
  }

  if (pStateFunc2) {
    pStateFunc2();
  }
}

/**
  * @brief  Return current state code.
  * @param  None
  * @retval State code
  */
es_state_t ES_GetState(void)
{
  return _State;
}

/**
  * @brief  Set current state to given code .
  * @param  State
  * @retval None
  */
void ES_SetState(es_state_t new_state)
{
  _State = new_state;
}
