/**
 *  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 */
  {stShowTime, evBTN1Pressed, stShowWDay, showWD,     NULL},
  {stShowWDay, evBTN1Pressed, stShowDM,   showDayMon, NULL},
  {stShowDM,   evBTN1Pressed, stShowYear, showYear,   NULL},
  {stShowYear, evBTN1Pressed, stShowTime, showTime,   NULL},
  {stShowTime, evBTN4Pressed, stShowSensorData, showSensorData, NULL},

  /* display wdt */
  {stShowWDay,        evDisplayWDT, stShowTime, showTime, NULL},
  {stShowDM,          evDisplayWDT, stShowTime, showTime, NULL},
  {stShowMDay,        evDisplayWDT, stShowTime, showTime, NULL},
  {stShowMon,         evDisplayWDT, stShowTime, showTime, NULL},
  {stShowYear,        evDisplayWDT, stShowTime, showTime, NULL},
  {stShowBright,      evDisplayWDT, stShowTime, showTime, NULL},
  {stShowHumidity,    evDisplayWDT, stShowTime, showTime, NULL},
  {stShowTemperature, evDisplayWDT, stShowTime, showTime, NULL},
  {stShowPressure,    evDisplayWDT, stShowTime, showTime, NULL},

  /* refresh output */
  {stShowTime, evNewSecond,  stNoChange, showTime,   NULL},
  {stShowWDay, evRefreshCal, stNoChange, showWD,     NULL},
  {stShowDM,   evRefreshCal, stNoChange, showDayMon, NULL},
  {stShowMDay, evRefreshCal, stNoChange, showDay,    NULL},
  {stShowMon,  evRefreshCal, stNoChange, showMonth,  NULL},
  {stShowYear, evRefreshCal, stNoChange, showYear,   NULL},

  #ifdef BLAH_BLAH_BLAH
  {stShowYear,   evBTN1Pressed, stShowBright, showBright, NULL},
  {stShowBright, evBTN1Pressed, stShowTime,   showTime,   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,    evBTN3Pressed, stNoChange, setHHInc,     setTimeShow},
  {stSetHH,    evBTN2Pressed, stNoChange, setHHDec,     setTimeShow},
  {stSetMM,    evBTN3Pressed, stNoChange, setMMInc,     setTimeShow},
  {stSetMM,    evBTN2Pressed, stNoChange, setMMDec,     setTimeShow},

  {stSetHH,    evNewSecond,   stNoChange, setTimeShow,  NULL},
  {stSetMM,    evNewSecond,   stNoChange, setTimeShow,  NULL},

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

  /* set calendar */
  {stShowWDM, evBTN1Holded,  stSetWDay,  setDateBegin,  setWDayBegin},
  {stSetWDay, evBTN1Holded,  stShowWDM,  setDateEnd,    showWDM},
  {stSetMDay, evBTN1Holded,  stShowWDM,  setDateEnd,    showWDM},
  {stSetMon,  evBTN1Holded,  stShowWDM,  setDateEnd,    showWDM},
  {stSetWDay, evBTN1Pressed, stSetMDay,  setMDayBegin,  NULL},
  {stSetMDay, evBTN1Pressed, stSetMon,   setMonthBegin, NULL},
  {stSetMon,  evBTN1Pressed, stSetWDay,  setWDayBegin,  NULL},
  {stSetWDay, evBTN2Pressed, stNoChange, setDecWDay,    setWDMShow},
  {stSetWDay, evBTN3Pressed, stNoChange, setIncWDay,    setWDMShow},
  {stSetMDay, evBTN2Pressed, stNoChange, setDecMDay,    setWDMShow},
  {stSetMDay, evBTN3Pressed, stNoChange, setIncMDay,    setWDMShow},
  {stSetMon,  evBTN2Pressed, stNoChange, setDecMonth,   setWDMShow},
  {stSetMon,  evBTN3Pressed, stNoChange, setIncMonth,   setWDMShow},

  {stShowYear, evBTN1Holded,  stSetYear,  setDateBegin, setYearBegin},
  {stSetYear,  evBTN1Holded,  stShowYear, setDateEnd,   showYear},
  {stSetYear,  evBTN2Pressed, stNoChange, setDecYear,   setYearShow},
  {stSetYear,  evBTN3Pressed, stNoChange, setIncYear,   setYearShow},

  {stSetWDay, evDisplayWDT, stShowTime, showTime, NULL},
  {stSetMDay, evDisplayWDT, stShowTime, showTime, NULL},
  {stSetMon,  evDisplayWDT, stShowTime, showTime, NULL},
  {stSetYear, evDisplayWDT, stShowTime, showTime, NULL},
  /*
  {stShowWDay, evBTN3Pressed, stNoChange, incWDay,  showWDM},
  {stShowMDay, evBTN3Pressed, stNoChange, incMDay,  showWDM},
  {stShowMon,  evBTN3Pressed, stNoChange, incMonth, showWDM},
  {stShowYear, evBTN3Pressed, stNoChange, incYear,  showYear},
  {stShowWDay, evBTN2Pressed, stNoChange, decWDay,  showWDM},
  {stShowMDay, evBTN2Pressed, stNoChange, decMDay,  showWDM},
  {stShowMon,  evBTN2Pressed, stNoChange, decMonth, showWDM},
  {stShowYear, evBTN2Pressed, stNoChange, decYear,  showYear},
  */
  /* set bright */
  {stShowBright, evBTN3Pressed, stNoChange, incBright, showBright},
  {stShowBright, evBTN2Pressed, stNoChange, decBright, showBright},
  #endif // BLAH_BLAH_BLAH
  /* 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 {
      TIM1->CCR2 = 0x0;
      TIM1->CCR3 = 0x0;
      TIM1->CCR4 = 0xff;
      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;
}
