#include "clock.h"
#include "utils.h"

/* type defs */
typedef struct t_btn {
  uint8_t     time;
  es_event_t  pressed;
  es_event_t  holded;
  uint32_t    pin;
  //(GPIO_TypeDef *) GPIOA; // ?->IDR
} btn_t;

/* variables */
rtc_t Clock;
static rtc_t setClock;
static btn_t Button[BTN_NUM] = {
  {0, evBTN1Pressed, evBTN1Holded,  BTN1_PIN},
  {0, evBTN2Pressed, evBTN2Pressed, BTN2_PIN},
  {0, evBTN3Pressed, evBTN3Pressed, BTN3_PIN} //,
  //{0, evBTN4Pressed, evBTN4Holded,  BTN4_PIN}
};
//convert linear bright level to logariphmic
const uint8_t cie[MAX_BRIGHT_LVL + 1] = {
  0, 2, 4, 8, 13, 20, 29, 40, 54, 72, 92, 116, 145, 177, 214, 255
};
volatile static uint8_t dispWDT = 0;
static uint8_t LightingBright;
static light_mode_t LightingMode;
static uint8_t LightingColour;
static flash_data_t Lighting, setLighting;

/* function prototypes */
static void check_DayNight(void);
static void HSV2LED(const uint8_t hue, const uint8_t val);
static void valIncrease(uint8_t * val, const uint8_t max);
static void valDecrease(uint8_t * val, const uint8_t max);
static void dvalIncrease(uint8_t * val, const uint8_t max);
static void dvalDecrease(uint8_t * val, const uint8_t max);
static void showRainbow(void);

/* funcions */
void Clock_Init(void) {
  RTC_ReadAll(&Clock);
  showTime();

  flash_result_t flash_res = Flash_Read(&Lighting.u64);
  if ((flash_res != Flash_Ok) || (Lighting.name.DayHour == 0xff)) {
    Lighting.name.DayHour = MORNING_HOUR;
    Lighting.name.NightHour = EVENING_HOUR;
    Lighting.name.DayBright = DAY_BR_LVL;
    Lighting.name.NightBright = NIGHT_BR_LVL;
    Lighting.name.DayMode = light_Rainbow;
    Lighting.name.NightMode = light_Colour;
    Lighting.name.NightColour = COLOUR_NIXIE;
  }

  check_DayNight();
}

static void check_DayNight(void) {
  if ((Clock.Hr >= Lighting.name.DayHour) && (Clock.Hr < Lighting.name.NightHour)) {
    Flag.Now_Day = 1;
    LightingBright = cie[Lighting.name.DayBright];
    LightingMode = Lighting.name.DayMode;
    LightingColour = Lighting.name.DayColour;
  } else {
    Flag.Now_Day = 0;
    LightingBright = cie[Lighting.name.NightBright];
    LightingMode = Lighting.name.NightMode;
    LightingColour = Lighting.name.NightColour;
  }

  //tube_BrightLevel(Tube_All, LightingBright);
  TUBES_BRIGHT(LightingBright);
}

/**
  * @brief  Обработка кнопок.
  * @param  : None
  * @retval : None
  */
void btnProcess(void) {
  /* get pin state */
  uint32_t pins = BTNS_STATE;

  int i;
  for (i=0; i<BTN_NUM; i++) {
    if ((pins & Button[i].pin) == 0) {
    /* button pressed */
      Button[i].time ++;
      if (Button[i].time >= (BTN_TIME_HOLDED/BTN_SCAN_PERIOD)) {
        Button[i].time -= (BTN_TIME_REPEATED/BTN_SCAN_PERIOD);
        if (Button[i].holded == Button[i].pressed) {
          /* if pressed and holded - same function, then button pressed auto repeat */
          ES_PlaceEvent(Button[i].pressed);
        }
      }
    } else if (Button[i].time != 0) {
    /* button released */
      if (Button[i].time >= ((BTN_TIME_HOLDED - BTN_TIME_REPEATED)/BTN_SCAN_PERIOD)) {
        /* process long press */
        ES_PlaceEvent(Button[i].holded);
      } else if (Button[i].time >= (BTN_TIME_PRESSED/BTN_SCAN_PERIOD)) {
        /* process short press */
        ES_PlaceEvent(Button[i].pressed);
      }
      Button[i].time = 0;
      RTOS_SetTask(btnProcess, BTN_SCAN_PAUSE, BTN_SCAN_PERIOD);
    }
  } /* end FOR */
}

void new_Second(void) {
  RTC_ReadAll(&Clock);

  // new hour
  if (Clock.Min == 0 && Clock.Sec == 0) {
    check_DayNight();
  }

  // check display watch dog timer
  if (dispWDT != 0) {
    dispWDT--;
    if (dispWDT == 0) {
      Blink_Stop();
      ES_PlaceEvent(evDisplayWDT);
    }
  }
}

/**
  * On/off symbols on IN-15 tube.
  */
void in15Off(void) {
  IN15_OFF;
  TUBE_C_OFF;
}

void in15Minus(void) {
  IN15_OFF;
  IN15_Minus;
  TUBE_C_ON;
}

void in15Plus(void) {
  IN15_OFF;
  IN15_Plus;
  TUBE_C_ON;
}

void in15Percent(void) {
  IN15_OFF;
  IN15_Percent;
  TUBE_C_ON;
}

void in15P(void) {
  IN15_OFF;
  IN15_P;
  TUBE_C_ON;
}

/**
 * @brief  HSV to RGB convertion
 * @param  hue: 0-59, sat: allways max, val (lightness): 0-255
 * @return none. RGB value output direct to LED.
 */
static void HSV2LED(const uint8_t hue, const uint8_t val) {
  uint32_t r=0, g=0, b=0;

  switch (hue / 10) {
  case 0:
    r = val;
      g = (val * hue) / 10;
      b = 0;
      break;
  case 1:
    r = (val * (10 - (hue % 10))) / 10;
    g = val;
    b = 0;
    break;
  case 2:
    r = 0;
    g = val;
    b = (val * (hue % 10)) / 10;
    break;
  case 3:
    r = 0;
    g = (val * (10 - (hue % 10))) / 10;
    b = val;
    break;
  case 4:
    r = (val * (hue % 10)) / 10;
    g = 0;
    b = val;
    break;
  case 5:
    r = val;
    g = 0;
    b = (val * (10 - (hue % 10))) / 10;
    break;
  }

  COLOR_R((uint8_t)r);
  COLOR_G((uint8_t)g);
  COLOR_B((uint8_t)b);
}

/**
  * Show info on tubes.
  */
void showTime(void) {
  uint8_t hue;
	in15Minus();
  RTOS_SetTask(in15Off, 500, 0);

  switch (LightingMode) {
  case light_Rainbow:
    hue = bcd2bin(Clock.Sec);
    HSV2LED(hue, LightingBright);
    break;

  case light_Colour:
    HSV2LED(LightingColour, LightingBright);
    break;
  
  default:
    LEDS_OFF;
    break;
  }

  tube4_t buf;
  buf.s8.tA = Clock.Hr >> 4;
  buf.s8.tB = Clock.Hr & 0xf;
  buf.s8.tD = Clock.Min >> 4;
  buf.s8.tE = Clock.Min & 0xf;
  showDigits(buf);
}

void showMMSS(void) {
	in15Minus();

  uint8_t hue = bcd2bin(Clock.Sec);
  HSV2LED(hue, LightingBright);

  tube4_t buf;
  buf.s8.tA = Clock.Min >> 4;
  buf.s8.tB = Clock.Min & 0xf;
  buf.s8.tD = Clock.Sec >> 4;
  buf.s8.tE = Clock.Sec & 0xf;
  showDigits(buf);
}

void showWD(void) {
  dispWDT = DISP_WDT_TIME;
  in15Off();

  tube4_t buf;
  buf.s8.tA = TUBE_BLANK;
  buf.s8.tB = Clock.WD & 0xf;
  buf.s8.tD = TUBE_BLANK;
  buf.s8.tE = TUBE_BLANK;
  showDigits(buf);
}

void showDayMon(void) {
  dispWDT = DISP_WDT_TIME;
  in15Off();

  tube4_t buf;
  buf.s8.tA = Clock.Day >> 4;
  buf.s8.tB = Clock.Day & 0xf;
  buf.s8.tD = Clock.Mon >> 4;
  buf.s8.tE = Clock.Mon & 0xf;
  showDigits(buf);
}

void showYear(void) {
  dispWDT = DISP_WDT_TIME;
  in15Off();

  tube4_t buf;
  buf.s8.tA = 2;
  buf.s8.tB = 0;
  buf.s8.tD = Clock.Year >> 4;
  buf.s8.tE = Clock.Year & 0xf;
  showDigits(buf);
}

void showHumidity(void) {
  dispWDT = DISP_WDT_TIME/2;
  HSV2LED(COLOUR_BLUE, LightingBright);
  in15Percent();

  tube4_t buf;
  buf.s8.tA = Humidity / 10;
  buf.s8.tB = Humidity % 10;
  buf.s8.tD = TUBE_BLANK;
  buf.s8.tE = TUBE_BLANK;
  showDigits(buf);
}

void showTemperature(void) {
  dispWDT = DISP_WDT_TIME/2;
  HSV2LED(COLOUR_RED, LightingBright);
  in15Plus();

  tube4_t buf;
  buf.s8.tA = TUBE_BLANK;
  buf.s8.tB = TUBE_BLANK;
  buf.s8.tD = Temperature / 10;
  buf.s8.tE = Temperature % 10;
  showDigits(buf);
}

void showPressure(void) {
  dispWDT = DISP_WDT_TIME/2;
  HSV2LED(COLOUR_GREEN, LightingBright);
  in15P();

  tube4_t buf;
  int tmp;
  buf.s8.tA = TUBE_BLANK;
  buf.s8.tB = Pressure / 100;
  tmp = Pressure % 100;
  buf.s8.tD = tmp / 10;
  buf.s8.tE = tmp % 10;
  showDigits(buf);
}

/* Simple function for cyclic show all sensor data */
void showSensorData(void) {
  in15Off();

  showTemperature();
  tdelay_ms(3000);

  showHumidity();
  tdelay_ms(3000);

  showPressure();
  tdelay_ms(2700);

  ES_SetState(stShowTime);
}

void setTimeShow(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  buf.s8.tA = setClock.Hr >> 4;
  buf.s8.tB = setClock.Hr & 0xf;
  buf.s8.tD = setClock.Min >> 4;
  buf.s8.tE = setClock.Min & 0xf;
  showDigits(buf);
}

void setTimeBegin(void) {
  in15Minus();
  HSV2LED(COLOUR_NIXIE, LightingBright);
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);
  RTC_ReadAll(&setClock);
}

void setHHBegin(void) {
  Flag.Blink_1 = 1;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  setTimeShow();
}

void setHHInc(void) {
  valIncrease(&setClock.Hr, 23);
}

void setHHDec(void) {
  valDecrease(&setClock.Hr, 23);
}

void setMMBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  setTimeShow();
}

void setMMInc(void) {
  valIncrease(&setClock.Min, 59);
}

void setMMDec(void) {
  valDecrease(&setClock.Min, 59);
}

void setTimeEnd(void) {
  dispWDT = 0;
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);

  setClock.Sec = 0;
  RTC_WriteTime(&setClock);

  Blink_Stop();

  RTC_ReadAll(&Clock);
}

void setDateBegin(void) {
  in15Off();
  HSV2LED(COLOUR_NIXIE, LightingBright);
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);
  RTC_ReadAll(&setClock);
}

void setDateEnd(void) {
  dispWDT =  0;
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);
  
  RTC_WriteCalendar(&setClock);

  Blink_Stop();

  RTC_ReadAll(&Clock);
}

void setWDBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  setWDShow();
}

void setWDShow(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  buf.s8.tA = TUBE_BLANK;
  buf.s8.tB = setClock.WD & 0xf;
  buf.s8.tD = TUBE_BLANK;
  buf.s8.tE = TUBE_BLANK;
  showDigits(buf);
}

void setDMShow(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  buf.s8.tA = setClock.Day >> 4;
  buf.s8.tB = setClock.Day & 0xf;
  buf.s8.tD = setClock.Mon >> 4;
  buf.s8.tE = setClock.Mon & 0xf;
  showDigits(buf);
}

void setYearShow(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  buf.s8.tA = 2;
  buf.s8.tB = 0;
  buf.s8.tD = setClock.Year >> 4;
  buf.s8.tE = setClock.Year & 0xf;
  showDigits(buf);
}

void setMDBegin(void) {
  Flag.Blink_1 = 1;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  setDMShow();
}

void setMonthBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  setDMShow();
}

void setYearBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  setYearShow();
}

void setIncWDay(void) {
  valIncrease(&setClock.WD, 7);
}

void setIncMDay(void) {
  valIncrease(&setClock.Day, 31);
}

void setIncMonth(void) {
  valIncrease(&setClock.Mon, 12);
}

void setIncYear(void) {
  valIncrease(&setClock.Year, 99);
}

void setDecWDay(void) {
  valDecrease(&setClock.WD, 7);
}

void setDecMDay(void) {
  valDecrease(&setClock.Day, 31);
}

void setDecMonth(void) {
  valDecrease(&setClock.Mon, 12);
}

void setDecYear(void) {
  valDecrease(&setClock.Year, 99);
}

void showDNhour(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  es_state_t e_st = ES_GetState();

  if (e_st == stShowDNhours){
    buf.s8.tA = Lighting.name.DayHour >> 4;
    buf.s8.tB = Lighting.name.DayHour & 0xf;
    buf.s8.tD = Lighting.name.NightHour >> 4;
    buf.s8.tE = Lighting.name.NightHour & 0xf;
  } else {
    buf.s8.tA = setLighting.name.DayHour >> 4;
    buf.s8.tB = setLighting.name.DayHour & 0xf;
    buf.s8.tD = setLighting.name.NightHour >> 4;
    buf.s8.tE = setLighting.name.NightHour & 0xf;
  }
  showDigits(buf);
}

void showDNbright(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  es_state_t e_st = ES_GetState();

  if (e_st == stShowDNbright){
    buf.s8.tA = Lighting.name.DayBright / 10;
    buf.s8.tB = Lighting.name.DayBright % 10;
    buf.s8.tD = Lighting.name.NightBright / 10;
    buf.s8.tE = Lighting.name.NightBright % 10;
  } else {
    buf.s8.tA = setLighting.name.DayBright / 10;
    buf.s8.tB = setLighting.name.DayBright % 10;
    buf.s8.tD = setLighting.name.NightBright / 10;
    buf.s8.tE = setLighting.name.NightBright % 10;
  }
  showDigits(buf);
}

void showDNmode(void) {
  dispWDT = DISP_WDT_TIME;

  uint8_t dm, nm; 
  tube4_t buf;
  es_state_t e_st = ES_GetState();

  if (e_st == stShowDNmode){
    dm = Lighting.name.DayMode;
    nm = Lighting.name.NightMode;
  } else {
    dm = setLighting.name.DayMode;
    nm = setLighting.name.NightMode;
  }

  buf.s8.tA = TUBE_BLANK;
  buf.s8.tB = dm & 0xf;
  buf.s8.tD = TUBE_BLANK;
  buf.s8.tE = nm & 0xf;
  showDigits(buf);
}

void showDNcolour(void) {
  dispWDT = DISP_WDT_TIME;

  tube4_t buf;
  es_state_t e_st = ES_GetState();

  if (e_st == stShowDNcolour){
    buf.s8.tA = Lighting.name.DayColour / 10;
    buf.s8.tB = Lighting.name.DayColour % 10;
    buf.s8.tD = Lighting.name.NightColour / 10;
    buf.s8.tE = Lighting.name.NightColour % 10;
  } else {
    buf.s8.tA = setLighting.name.DayColour / 10;
    buf.s8.tB = setLighting.name.DayColour % 10;
    buf.s8.tD = setLighting.name.NightColour / 10;
    buf.s8.tE = setLighting.name.NightColour % 10;
  }
  showDigits(buf);
}

void setDNbegin(void) {
  in15Off();
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);
  setLighting.u64 = Lighting.u64;
  //Flash_Read(&setLighting.u64);
}

void setDNend(void) {
  dispWDT =  0;
  RTOS_SetTask(btnProcess, BTN_TIME_HOLDED, BTN_SCAN_PERIOD);
  Blink_Stop();
  LEDS_OFF;
  Flash_Write(&setLighting.u64);
  Lighting.u64 = setLighting.u64;
  check_DayNight();
}

void setDayHourBegin(void) {
  LEDS_OFF;
  Flag.Blink_1 = 1;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  showDNhour();
}

void setNightHourBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  showDNhour();
}

void setDayBrightBegin(void) {
  Flag.Blink_1 = 1;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  showDNbright();
}

void setNightBrightBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  showDNbright();
}

void setDayModeBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  showDNmode();
}

void setNightModeBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 1;
  Blink_Start();
  showDNmode();
}

void setDayColourBegin(void) {
  Flag.Blink_1 = 1;
  Flag.Blink_2 = 1;
  Flag.Blink_4 = 0;
  Flag.Blink_5 = 0;
  Blink_Start();
  HSV2LED(setLighting.name.DayColour, LightingBright);
  showDNcolour();
}

void setNightColourBegin(void) {
  Flag.Blink_1 = 0;
  Flag.Blink_2 = 0;
  Flag.Blink_4 = 1;
  Flag.Blink_5 = 1;
  Blink_Start();
  HSV2LED(setLighting.name.NightColour, LightingBright);
  showDNcolour();
}

void setIncDayHour(void) {
  valIncrease(&setLighting.name.DayHour, 23);
}

void setIncDayBright(void) {
  dvalIncrease(&setLighting.name.DayBright, MAX_BRIGHT_LVL);
  //tube_BrightLevel(Tube_All, setLighting.name.DayBright);
  TUBES_BRIGHT(setLighting.name.DayBright);
}

void setIncDayMode(void) {
  dvalIncrease(&setLighting.name.DayMode, MAX_LIGHT_MODE);
  if (setLighting.name.DayMode == light_Rainbow) {
    RTOS_SetTask(showRainbow, 0, 20);
  } else {
    RTOS_DeleteTask(showRainbow);
  }
}

void setIncDayColour(void) {
  dvalIncrease(&setLighting.name.DayColour, MAX_COLOR_VAL);
  HSV2LED(setLighting.name.DayColour, LightingBright);
}

void setDecDayHour(void) {
  valDecrease(&setLighting.name.DayHour, 23);
}

void setDecDayBright(void) {
  dvalDecrease(&setLighting.name.DayBright, MAX_BRIGHT_LVL);
  //tube_BrightLevel(Tube_All, setLighting.name.DayBright);
  TUBES_BRIGHT(setLighting.name.DayBright);
}

void setDecDayMode(void) {
  dvalDecrease(&setLighting.name.DayMode, MAX_LIGHT_MODE);
  if (setLighting.name.DayMode == light_Rainbow) {
    RTOS_SetTask(showRainbow, 0, 20);
  } else {
    RTOS_DeleteTask(showRainbow);
  }
}

void setDecDayColour(void) {
  dvalDecrease(&setLighting.name.DayColour, MAX_COLOR_VAL);
  HSV2LED(setLighting.name.DayColour, LightingBright);
}

void setIncNightHour(void) {
  valIncrease(&setLighting.name.NightHour, 23);
}

void setIncNightBright(void) {
  dvalIncrease(&setLighting.name.NightBright, MAX_BRIGHT_LVL);
  //tube_BrightLevel(Tube_All, setLighting.name.NightBright);
  TUBES_BRIGHT(setLighting.name.NightBright);
}

void setIncNightMode(void) {
  dvalIncrease(&setLighting.name.NightMode, MAX_LIGHT_MODE);
  if (setLighting.name.NightMode == light_Rainbow) {
    RTOS_SetTask(showRainbow, 0, 20);
  } else {
    RTOS_DeleteTask(showRainbow);
  }
}

void setIncNightColour(void) {
  dvalIncrease(&setLighting.name.NightColour, MAX_COLOR_VAL);
  HSV2LED(setLighting.name.NightColour, LightingBright);
}

void setDecNightHour(void) {
  valDecrease(&setLighting.name.NightHour, 23);
}

void setDecNightBright(void) {
  dvalDecrease(&setLighting.name.NightBright, MAX_BRIGHT_LVL);
  //tube_BrightLevel(Tube_All, setLighting.name.NightBright);
  TUBES_BRIGHT(setLighting.name.NightBright);
}

void setDecNightMode(void) {
  dvalDecrease(&setLighting.name.NightMode, MAX_LIGHT_MODE);
  if (setLighting.name.NightMode == light_Rainbow) {
    RTOS_SetTask(showRainbow, 0, 20);
  } else {
    RTOS_DeleteTask(showRainbow);
  }
}

void setDecNightColour(void) {
  dvalDecrease(&setLighting.name.NightColour, MAX_COLOR_VAL);
  HSV2LED(setLighting.name.NightColour, LightingBright);
}

void setDNbreak(void) {
  // restore led and tube bright level
  check_DayNight();
}

/**
  * @brief  Increase BCD value.
  * @param  : val, max
  * @retval : None
  */
static void valIncrease(uint8_t * val, uint8_t max) {
  uint8_t bin = 10 * (*val >> 4) + (*val & 0x0f);
  if (bin < max) {
    bin ++;
  } else {
    bin = 0;
  }
  *val = ((bin / 10 ) << 4) | (bin % 10);
}

/**
  * @brief  Decrease BCD value.
  * @param  : value, max
  * @retval : None
  */
static void valDecrease(uint8_t * val, uint8_t max) {
  uint8_t bin = 10 * (*val >> 4) + (*val & 0x0f);
  if (bin > 0) {
    bin --;
  } else {
    bin = max;
  }
  *val = ((bin / 10 ) << 4) | (bin % 10);
}

/**
  * @brief Increase/Decrease decimal value.
  * @param val
  * @param max 
  * @retval : None, change value
  */
static void dvalIncrease(uint8_t * val, uint8_t max) {
  if (*val < max) {
    *val += 1;
  } else {
    *val = 0;
  }
}

static void dvalDecrease(uint8_t * val, uint8_t max) {
  if (*val > 0) {
    *val -= 1;
  } else {
    *val = max;
  }
}

static void showRainbow(void) {
  static uint8_t hue = 0;

  HSV2LED(hue, LightingBright);

  if (hue < 59) {
    hue ++;
  } else {
    hue = 0;
    RTOS_DeleteTask(showRainbow);
  }
}
