// Attempt at non-blocking version of FastLED classesTimer.ino example
// https://wokwi.com/projects/356666059490147329
// Discussion at 
// https://forum.arduino.cc/t/make-some-fastled-code-non-blocking/1090335
//
// Based on sim from https://wokwi.com/projects/356665860602531841
// with code from https://github.com/s-marley/FastLED-basics/tree/main/5.%20Multiple%20patterns/classesTimer
// using a wokwi-neopixel-canvas
//
// Contribution of SRAM monitor from qbits-us per 
// https://forum.arduino.cc/t/make-some-fastled-code-non-blocking/1090335/2
//
// Try to make non-blocking
//
// On Nano:
// A0 potentiometer: led speed 1-600 sec
// A1 potentiometer: report speed 1-1000 ms
// 
// Note that the reading of the A0 report() happens frequently while the loop animations are going 

#include <FastLED.h>

#define NUM_LEDS  18
const int LED_PIN  = 2;
const int LED_speed_pin = A0; 
const int report_speed_pin = A1; 
const int verbose = 4;

CRGB leds[NUM_LEDS];
uint8_t patternCounter = 0;
bool isRunning = false;
bool changePattern = false;

void checkTimer();
int reportInterval = 500; // ms
int ledInterval = 1; // sec

#include "Fire2012.h"
#include "Pacifica.h"
#include "Pride2015.h"

void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(100);
  Serial.begin(115200);
  pinMode(LED_speed_pin,INPUT);  
  pinMode(report_speed_pin,INPUT);  
}

void loop() {
  
  handleIO();

  switch (patternCounter) {
    case 0:
      runFire2012();
      break;
    case 1:
      if(1) runPacificaStatic(); else // XXX comment to use runPacifica
       runPacifica();
      break;
    case 2:
      runPride2015();
      break;
  }
  report();
  if(verbose >=0) LoopCounter();
}

/////////// diagnostic code for excercising doing several things at once 
// An output line like this consists of several reports
// Pride2015.- SRAM left: 1514 400Hzdeleted
// ^ object intantiated
//          ^ report triggered
//             ^ Free SRAM report
//                             ^ loop() frequency
//                                  ^ object deleted
void report(){
  static uint32_t last=0;
  //const int interval=1;
  if(millis() - last > reportInterval){
    last+=reportInterval;
    if(verbose >= 3)Serial.print('.');
    if(verbose >= 2)display_freeram();
  }
}

int lastFreeRam = 0;

void display_freeram() {
  int ramleft = freeRam();
  if (ramleft!= lastFreeRam)
  {
   lastFreeRam = ramleft; 
  Serial.print(F("- SRAM left: "));
  Serial.print(ramleft);
  Serial.print(' ');
  }
}

int freeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0  
    ? (int)&__heap_start : (int) __brkval);  
}

void LoopCounter() // tells the average response speed of void loop()
// code modified from https://forum.arduino.cc/t/while-loop-as-a-condition-for-limit-switches/963899/11
{ // inside a function, static variables keep their value from run to run
  const unsigned long microsInOneSecond=1000000UL;
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.print( count ); // 32-bit binary into decimal text = many micros
    Serial.print("Hz");
    count = 0; // don't forget to reset the counter 
  }
}
//////////// end diagnostic code 


void handleIO(void){
   const int interval = 250; //ms
   static uint32_t last = -interval;
  if(millis() - last >= interval){
    ledInterval = map(analogRead(LED_speed_pin),0,1024,1,600); // seconds
    reportInterval = map(analogRead(report_speed_pin),0,1024,1,1000); // ms
  }
}

void checkTimer() {
  static uint32_t last = 0;
  if(millis() - last >= ledInterval*1000UL){
    last = millis();
    nextPattern();
  }
}

void nextPattern() {
  isRunning = false;
  changePattern = true;
  //patternCounter = (patternCounter + 1) % 3;
}

// These runXXXX commands were modified from the original to
// be non-blocking by keeping a static pointer to an object
// that persists between calls, and is destryed when finished.

void runFire2012(){
  static Fire2012 *fire2012;
  if(! fire2012) {
    isRunning = true;
    fire2012 = new Fire2012;
    if(verbose>1)Serial.print(F("Fire2012"));
  }
  if(changePattern){ // shut things down and transition
    isRunning = false;
    if(fire2012){ // de-allocate
      delete fire2012;
      fire2012=NULL;
      if(verbose>1)Serial.print(F("deleted\n"));
    }
    patternCounter = (patternCounter+1)%3;
    changePattern=false; // oneshot
  }
//  Fire2012 fire2012 = Fire2012();
  if(isRunning) fire2012->runPattern();
}

void runPacificaStatic(){
  static bool wasRunning = false;
  static Pacifica pacifica;
  if(wasRunning == false){
    isRunning = true;
    wasRunning = isRunning;
    if(verbose>1)Serial.print(F("Pacifica"));
  }
  if(changePattern){
    isRunning = false;
    wasRunning = false;
      if(verbose>1)Serial.print(F("Over\n"));
    patternCounter = (patternCounter+1)%3;
    changePattern=false; // oneshot
  }
  if(isRunning) pacifica.runPattern();
}

void runPacifica(){
  static Pacifica *pacifica;
//  if( !isRunning || ! pacifica) {
  if( ! pacifica) {
    isRunning = true;
    pacifica = new Pacifica;
    if(verbose>1)Serial.print(F("Pacifica"));
  }
  if(changePattern){ // shut things down and transition
    isRunning = false;
    if(pacifica){
      delete pacifica;
      pacifica = NULL;
      if(verbose>1)Serial.print(F("deleted\n"));
    }
    patternCounter = (patternCounter+1)%3;
    changePattern=false; // oneshot
  }
  if(isRunning) pacifica->runPattern();
}

void runPride2015(){
  static Pride2015 *pride2015;
  if( ! pride2015) {
    isRunning = true;
    pride2015 = new Pride2015;
    if(verbose>1)Serial.print(F("Pride2015"));
  }
  if(changePattern){ // shut things down and transition
    isRunning = false;
    if(pride2015){
      delete pride2015;
      pride2015 = NULL;
      if(verbose>1)Serial.print(F("deleted\n"));
   }
    patternCounter = (patternCounter+1)%3;
    changePattern=false; // oneshot
  }
  if(isRunning) pride2015->runPattern();
}
Pot1: LED Pattern change delay 0-600s
Pot2: Report Speed