#include <Adafruit_NeoPixel.h>
# ifndef WDTCSR
# define WDTCSR WDTCR
# endif // WDTCSR

enum { PIN = A3, NUMPIXELS = 6, FULLY_SATURATED = 255 };

typedef struct RgbColor
{
  unsigned char r;
  unsigned char g;
  unsigned char b;
} RgbColor;

typedef struct HsvColor
{
  unsigned char h;
  unsigned char s;
  unsigned char v;
  HsvColor( void ) { h = s = v = 0; }
  HsvColor( int a, int b, int c )
  {
    h = a;
    s = b;
    v = c;
  }
} HsvColor;

RgbColor HsvToRgb( HsvColor hsv )
{
  RgbColor rgb;

  if( hsv.s == 0 )
  {
    rgb.r = hsv.v;
    rgb.g = hsv.v;
    rgb.b = hsv.v;
    return rgb;
  }

  // converting to 16 bit to prevent overflow
  int h = hsv.h;
  int s = hsv.s;
  int v = hsv.v;

  char region = h / 43;
  int remainder = (h - (region * 43)) * 6;

  char p = (v * (255 - s)) >> 8;
  char q = (v * (255 - ((s * remainder) >> 8))) >> 8;
  char t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;

  switch (region)
  {
    case 0:
      rgb.r = v;
      rgb.g = t;
      rgb.b = p;
      break;
    case 1:
      rgb.r = q;
      rgb.g = v;
      rgb.b = p;
      break;
    case 2:
      rgb.r = p;
      rgb.g = v;
      rgb.b = t;
      break;
    case 3:
      rgb.r = p;
      rgb.g = q;
      rgb.b = v;
      break;
    case 4:
      rgb.r = t;
      rgb.g = p;
      rgb.b = v;
      break;
    default:
      rgb.r = v;
      rgb.g = p;
      rgb.b = q;
      break;
  }

  return rgb;
}

Adafruit_NeoPixel pixels = Adafruit_NeoPixel( NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800 );

void
setup( void )
{
  DDRB = 0;                   // Unused PORT B set to output to save power
  PORTB = 0;                  // Unused PORT B pins set to low to save power
  ADCSRA = 0;                 // Disable the ADC to save power

  pixels.begin();
}

#include <avr/sleep.h>
#include <avr/wdt.h>

typedef enum period
{
  TP_16MS, TP_32MS, TP_64MS, TP_128MS, TP_250MS, TP_500MS, TP_1SEC, TP_2SEC, TP_4SEC, TP_8SEC
};

void
setup_watchdog( period p )
{
  byte watchdog_mask;

  if( p > TP_8SEC )
    p = TP_8SEC;

  watchdog_mask = p & 7;

  if( p > 7 )
    watchdog_mask |= (1<<5);

  watchdog_mask |= (1<<WDCE);

  MCUSR &= ~(1<<WDRF);
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = watchdog_mask;
  WDTCSR |= _BV(WDIE);
}

ISR( WDT_vect )
{
}

int save_adc;

void
wakeup_on_watchdog( period p )
{
  setup_watchdog( p );

  save_adc = ADCSRA;
  ADCSRA = 0;       // Turn off ADC

  set_sleep_mode( SLEEP_MODE_PWR_DOWN );
  sleep_enable();
  sleep_mode();

  //
  // Execution resumed hare after watchdog wakes up the processor
  //

  sleep_disable();
  ADCSRA = save_adc;
}


void
loop( void )
{
  pixels.setBrightness( 255 );
  int which = random( NUMPIXELS );
  for( int i = 0; i < NUMPIXELS; i++ )
  {
    int bright = 0;
    if( i == which )
        bright = 255;
    unsigned char hue = random( 255 );
    HsvColor hsv( hue, FULLY_SATURATED, bright );
    RgbColor rgb = HsvToRgb( hsv );
    if( random( 15 ) == 1 )
      pixels.setPixelColor( i, pixels.Color( 0, 0, 0, 255 ) );
    else
      pixels.setPixelColor( i, pixels.Color( rgb.r, rgb.g, rgb.b, 0 ) );
  }
  pixels.show();
  delay( 1 );
  pixels.setBrightness( 0 );
  pixels.show();
  wakeup_on_watchdog( TP_250MS );
}

