/******************************************************************************
 * Addressable LED Strip Light Saber Example
 * By Joe Larson, the 3D Printing Professor
 * http://3dpprofessor.com
 * 
 * The light saber's blade is 50 individually addressable RGB LEDs on a strip.
 * 
 * This example code is designed to help you create your own light saber blade
 * patterns that can be quickly added to the 3DPProfessor's official light saber.
 * Simply take the "yourBlade()" function, rename it something meaningful, 
 * and write your function. Then, submit your code to [email protected]
 * and it may be included in a future video on the 3DPProfessor youtube 
 * channel.
 ******************************************************************************/
#include "FastLED.h"

/***************************************
 *        Hardware definitions         *
 ***************************************/
#define NUM_LEDS 50
#define LED_DATA_PIN 3
#define POT_PIN A3
/***************************************/

CRGB leds[NUM_LEDS];

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("LED Strip Light Saber Example");
  Serial.println("By Joe Larson the 3D Printing Professor");
  Serial.println("http://3dpprofessor.com");

  pinMode(POT_PIN, INPUT);

  randomSeed(analogRead(A0));

  FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(leds, NUM_LEDS);
}

int curmode = 0;
const int numModes = 2;

void loop() 
{
  curmode = getMode();
  
  switch (curmode) 
  {
    case 1:
      Serial.println("Generic Blade");
      genericBlade();
      break;
    case 2:
      Serial.println("Your Blade");
      yourBlade();
      break;
    default:
      Serial.println("Blade is starting in dead zone. Turn the pot a little");
      delay(200);
  }
}

void genericBlade()
{
  // Here's a basic example you can build off of.
  CRGB color = getSubColor();
  // You can start with a transition to make the change less abrupt.
  // In this case I'm just "extending" the blade.
  Serial.println("Transition");
  for (int i = 0; i < NUM_LEDS; i++)
  {
    leds[i] = color;
    FastLED.show();
    delay(20);
  }
  
  while (!isTimeToChange())
  {
    delay (1200);
    color = getSubColor();
    // At this point you can do any animations you want, and they'll loop
    // until the pot is twisted to a new settings.
    for (int pulse = 0; pulse < 12; pulse++)
    {
        int pulsefactor = 4 * pulse;
        CRGB color = getSubColor() + CRGB(pulsefactor, pulsefactor, pulsefactor);
        fill_solid(leds, NUM_LEDS, color);
        FastLED.show();
        delay(10);

    }
    for (int pulse = 12; pulse >=0; pulse--)
    {
        int pulsefactor = 4 * pulse;
        CRGB color = getSubColor() + CRGB(pulsefactor, pulsefactor, pulsefactor);
        fill_solid(leds, NUM_LEDS, color);
        FastLED.show();
        delay(20);
    }
  }
}

CRGB getSubColor()
{
  return CHSV (getFineMode(255), 255, 255);
}

void yourBlade() {
  // So what are you going to do?
}

bool isTimeToChange()
{
  return getMode() != curmode;
}

/**************************************************/
/*      getMode global calulation                 */
const float range = 1024.0 / numModes;
/**************************************************/
int getMode() 
{
  /*****************************************************************************
   * Getmode changes the pot's 0-1024 input into one of numModes zone.
   * But it's tricky (because of course it is). Zone 1 is the first 255, so that
   * the pot data can be used to change through all the colors. Then the rest of
   * the zones are divided evenly among the remaining range with a "dead zone"
   * of 2 spaces between each, to overcome the "shake" that pots sometimes
   * return when straddling two inputs. If it's in that dead zone, it just echos
   * curmode value.
   *****************************************************************************/
  int val = analogRead(POT_PIN);
  
  int zone = (val/range)+1;
  
  if (val > 3 && val%(int)range < 2) // If it's close to the division edge, return "dead"
  {
    Serial.println("Returning curmode");
    return curmode;
  }
  return zone;
}

int getFineMode(int numZones)
{
  /*****************************************************************************
   * Returns between 0 and numZones-1 based on the pot's position within the
   * coarser "zone" returned in GetMode above.
   *****************************************************************************/
  int val = analogRead(POT_PIN);
  int zone = val/range;
  int fineVal = val - (range * zone);
  return fineVal*((float)numZones/range);
}