#include <math.h>
#include "FastLED.h"

const byte pinData = 6;
const int timeIntervalSampling = 30; // Sample window width (50 ms would be 20Hz)
const int levelShift = 512;

const byte ledCount = 24;
const byte ledBrightness = 255;

struct CRGB ledRing[ledCount];
struct CRGB ledGradient[ledCount];
CHSV gradientHueStart = CHSV(96, 255, 255);
CHSV grandientHueEnd = CHSV(0, 255, 255);

//int sampleCounter;
int audioInRaw;
int audioInRectified;
float audioFiltered;
//float audioRMSPrecalc;
//float audioRMS;

const int intervalPeakHold = 600; // Peak hold interval in milliseconds
const byte intervalPeakDecay = 20; // Peak decay interval in milliseconds

int newPeak; // New peak value (= number of LEDs from 0 to peak)
int previousPeak;
unsigned long timeOfPeak; // Timestamp that updates with each new peak
bool peakDecay = false; // Toggles if the peak LED can decay or not
byte peakBrightness;

void setup()
{
  Serial.begin(115200);

  FastLED.addLeds<NEOPIXEL, pinData>(ledRing, ledCount);
  FastLED.setBrightness(ledBrightness);

  fill_gradient(ledGradient, 0, gradientHueStart, ledCount, grandientHueEnd, SHORTEST_HUES);
}

void loop()
{
  unsigned long timeSamplingStart = millis();

  uint16_t audioPeakToPeak = 0;
  uint16_t audioMin = 1023;
  uint16_t audioMax = 0;

  while (millis() - timeSamplingStart < timeIntervalSampling)
  {
    audioInRaw = analogRead(0);
    audioInRectified = abs(audioInRaw - levelShift);

    audioMin = min(audioInRectified, audioMin);
    audioMax = max(audioInRectified, audioMax);

    // Only for dB(SPL) calculation
    //audioRMSPrecalc += ((long) audioInRectified * audioInRectified); // Square each value and add them
    //sampleCounter ++;
  }

  //Serial.println(audioInRaw);
  //Serial.println(audioInRectified);

  //Serial.print(audioMin);
  //Serial.println(audioMax);

  // Only for dB(SPL) calculation
  //audioRMSPrecalc /= sampleCounter; // Divide by number of samples taken
  //audioRMS = sqrt(audioRMSPrecalc); // Take the square root
  //Serial.print("audioRMS:"); Serial.println(audioRMS);
  //sampleCounter = 0;

  audioPeakToPeak = audioMax - audioMin;
  if (audioPeakToPeak <= 7) // Push down to silence level
    audioPeakToPeak = 0;
  //Serial.print(audioPeakToPeak);

  audioFiltered = 0.8 * audioFiltered + (1 - 0.8) * audioPeakToPeak; // Leaky integrator, fast attack and slow decay
  //Serial.print(audioFiltered);

  // 10 and 150 are nasty magic numbers, this needs auto-range code later
  newPeak = constrain(map(audioFiltered, 10, 150, 0, ledCount), 0, ledCount) - 1;
  //Serial.print(newPeak);

  // Peak hold and decay algorithm
  if (newPeak >= previousPeak)
  {
    previousPeak = newPeak;
    timeOfPeak = millis();
    peakDecay = false;
  }

  else if (!peakDecay && (millis() - timeOfPeak > intervalPeakHold))
  {
    peakDecay = true;
    timeOfPeak += intervalPeakHold - intervalPeakDecay;
    peakBrightness = 255;
  }

  else if (peakDecay && (millis() - timeOfPeak > intervalPeakDecay))
  {
    if (previousPeak >= 0)
    {
      previousPeak--;
      timeOfPeak += intervalPeakDecay;
    }
  }

  FastLED.clear();

  for ( byte i = 0; i <= newPeak; i++)
  {
    ledRing[i] = ledGradient[i];
  }

  ledRing[previousPeak] = CHSV(0, 255, peakBrightness);

  FastLED.show();
}