/// \file PinballGame/StrandGraphics.cpp

/// \brief Real-time graphics functions for a WS2801 RGB LED strand.

/// \copyright No copyright, 2016-2017, Garth Zeglin.  This file is explicitly placed in the public domain.

/// \details This is a collection of functions compiled with the main pinball
/// game code.  Since only one LED strand is supported, these are all global
/// functions with global state.

#include "Arduino.h"
#include "Adafruit_WS2801.h"
#include "StrandGraphics.h"
  
// The 'strand' object is declared in the main file with type Adafruit_WS2801 strand.
extern Adafruit_WS2801 strand;

/// LED animation state.
static long strand_interval = 50000; /// microseconds between frames
static long strand_timer = 0;        /// microseconds until the next frame
static int strand_frame = 0;         /// current animation frame count

/// Utility function to create a 24 bit RGB color value encoded in a 32 bit integer.
static uint32_t strand_color(byte r, byte g, byte b)
{
  uint32_t c;
  c = r;
  c <<= 8;
  c |= g;
  c <<= 8;
  c |= b;
  return c;
}

/// Utility function to compute a 32 bit RGB color value from an 8-bit phase.
/// The colors are a transition from r -> g -> b -> r ...
static uint32_t strand_color_wheel(byte color_phase)
{
  if (color_phase < 85) {
    return strand_color(color_phase * 3, 255 - color_phase * 3, 90);
  } else if (color_phase < 170) {
    color_phase -= 85;
    return strand_color(255 - color_phase * 3, 90, color_phase * 3);
  } else {
    color_phase -= 170;
    return strand_color(90, color_phase * 3, 255 - color_phase * 3);
  }
}

/// Display one frame of an animated color spectrum.  The frame value can
/// continuously increment for each frame update.
static void strand_show_rainbow(int frame)
{
  int i;
  for (i = 0; i < strand.numPixels(); i++) {
    // tricky math! we use each pixel as a fraction of the full 96-color wheel
    // (thats the i / strand.numPixels() part)
    // Then add in frame which makes the colors go around per pixel
    // the % 96 is to make the wheel cycle around
    strand.setPixelColor(i, strand_color_wheel( ((i * 256 / strand.numPixels()) + frame) % 256) );
  }
  strand.show();   // write all the pixels out
}

/// Poll the LED strand timer and update the hardware with new animation as needed.
void strand_update(unsigned long interval)
{
  // Subtract the elapsed time from the counter until the right amount of time
  // has elapsed; this will keep the update rate more constant as the execution
  // rate varies.
  strand_timer -= interval;
  if (strand_timer < 0){
    strand_timer += strand_interval;
    strand_show_rainbow(strand_frame++);
  }
}

const int FAST_LEDS = 50000;
const int SLOW_LEDS = 100000;

void strand_set_fast_animation(void)
{
  strand_interval = FAST_LEDS;
}

void strand_set_slow_animation(void)
{
  strand_interval = SLOW_LEDS;
}
