#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978

const int OFF_STATE = 0;
const int ON_STATE = 1;
const int FINISH_STATE = 2;

int potiPIN = A0;
int photoPIN = A1;
int tempPIN = A2;
int buttonPIN = 2;

int piezoPIN = 9;
int led1PIN = 5;
int led2PIN = 6;
int led3PIN = 10;

int led1State = LOW;
int led2State = LOW;
int led3State = LOW;

int led1Interval = 100;
int led2Interval = 100;
int led3Interval = 100;
int piezoInterval = 1250;

int ledBlinkDurationMin = 5;
int ledBlinkDurationMax = 30;
int ledBlinkBrightnessMin = 0;
int ledBlinkBrightnessMax = 3;
int ledBrightness = 127;

unsigned long led1PreviousMillis = 0;
unsigned long led2PreviousMillis = 0;
unsigned long led3PreviousMillis = 0;
unsigned long piezoPreviousMillis = 0;

int pitch = NOTE_A3;
int duration = 100;
int shortDuration = 50;

int solutionTemperatureMax = 42;
int solutionTemperatureMin = 35;
int solutionPhotoMax = 1023;
int solutionPhotoMin = 950;
int solutionPotiMax = 840;
int solutionPotiMin = 780;

int currState = 0;
bool buttonPressed = false;

bool temperatureSolved = false;
bool photoSolved = false;
bool potiSolved = false;

float temperatureSmoothed = 0;
float photoSmoothed = 0;
float potiSmoothed = 0;
float smooth = 20;

void setup() {
  Serial.begin(9600);
  
  pinMode(piezoPIN, OUTPUT);
  pinMode(led1PIN, OUTPUT);
  pinMode(led2PIN, OUTPUT);
  pinMode(led3PIN, OUTPUT);

  int potiSmoothed = analogRead(potiPIN);
  int photoSmoothed = analogRead(photoPIN);
  int temperatureSmoothed = toDegreesCelsius(analogRead(tempPIN));

  currState = OFF_STATE;
}

void loop () {
  if (currState == OFF_STATE)
  {
      if (buttonPressed)
      {
        currState = ON_STATE;
        buttonPressed = false;
      }
  }
  else if (currState == ON_STATE)
  {
    if (buttonPressed)
    {
      currState = OFF_STATE;
      buttonPressed = false;
    }
    else
    {
      unsigned long currentMillis = millis();
      
      int potiValue = analogRead(potiPIN);
      int photoValue = analogRead(photoPIN);
      int tempValue = analogRead(tempPIN);
      float temperature = toDegreesCelsius(tempValue);
    
      piezoInterval = potiValue + duration;

      temperatureSmoothed = (temperatureSmoothed * (smooth-1)/smooth) + (temperature * (1 / smooth));
      photoSmoothed = (photoSmoothed * (smooth-1)/smooth) + (photoValue * (1 / smooth));
      potiSmoothed = (potiSmoothed * (smooth-1)/smooth) + (potiValue * (1 / smooth));

      // TEMPERATURE
      if (temperatureSmoothed > solutionTemperatureMin && temperatureSmoothed < solutionTemperatureMax)
      {
        analogWrite(led1PIN, ledBrightness);
        temperatureSolved = true;
      }
      else
      {
        temperatureSolved = false;
        if (currentMillis - led1PreviousMillis > led1Interval)
        {
          ledLoop1 ();
          led1PreviousMillis = currentMillis;
        }
      }

      // PHOTO
      if (photoSmoothed > solutionPhotoMin)
      {
        analogWrite(led2PIN, ledBrightness);
        photoSolved = true;
      }
      else
      {
        photoSolved = false;
        
        if (currentMillis - led2PreviousMillis > led2Interval)
        {
          ledLoop2 ();
          led2PreviousMillis = currentMillis;
        }
      }

      if (potiSmoothed > solutionPotiMin && potiSmoothed < solutionPotiMax)
      {
        analogWrite(led3PIN, ledBrightness);
        potiSolved = true;
      }
      else
      {
        potiSolved = false;
        
        if (currentMillis - led3PreviousMillis > led3Interval)
        {
          ledLoop3 ();
          led3PreviousMillis = currentMillis;
        }
      }

      if (currentMillis - piezoPreviousMillis > piezoInterval)
      {
        piezoLoop ();
        piezoPreviousMillis = currentMillis;
      }
    }
    
    if (temperatureSolved && photoSolved && potiSolved)
    {
      currState = FINISH_STATE;
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      tone (piezoPIN, NOTE_A4, duration);
      delay(duration);
      tone (piezoPIN, NOTE_D5, duration*2);
      delay(duration*2);
      delay(1000);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, 0);
      delay(333);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      delay(333);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, 0);
      delay(333);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      delay(333);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, 0);
      delay(333);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      delay(500);
    }
  }
  else if (currState == FINISH_STATE)
  {
    if (buttonPressed)
    {
      buttonPressed = false;
      currState = OFF_STATE;
    }
    else
    {
      delay(500);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, 0);
      delay(1000);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, 0);
      tone (piezoPIN, NOTE_A1, duration);
      delay(1000);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, 0);
      tone (piezoPIN, NOTE_B1, duration);
      delay(1000);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, 0);
      tone (piezoPIN, NOTE_C2, duration);
      delay(1000);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, ledBrightness);
      tone (piezoPIN, NOTE_D2, duration);
      delay(1000);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, 0);
      analogWrite(led1PIN, ledBrightness);
      tone (piezoPIN, NOTE_E2, duration);
      delay(1000);
      analogWrite(led3PIN, 0);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      tone (piezoPIN, NOTE_F2, duration);
      delay(1000);
      analogWrite(led3PIN, ledBrightness);
      analogWrite(led2PIN, ledBrightness);
      analogWrite(led1PIN, ledBrightness);
      tone (piezoPIN, NOTE_G2, duration);
      delay(500);
    }
  }

  int buttonValue = digitalRead(buttonPIN);

  if (buttonValue == 1)
  {
    buttonPressed = true;
  }
}

void ledLoop1 ()
{
  analogWrite(led1PIN, random(ledBlinkBrightnessMin,ledBlinkBrightnessMax));
  led1Interval = random(ledBlinkDurationMin,ledBlinkDurationMax);
}

void ledLoop2 ()
{
  analogWrite(led2PIN, random(ledBlinkBrightnessMin,ledBlinkBrightnessMax));
  led2Interval = random(ledBlinkDurationMin,ledBlinkDurationMax);
}

void ledLoop3 ()
{
  analogWrite(led3PIN, random(ledBlinkBrightnessMin,ledBlinkBrightnessMax));
  led3Interval = random(ledBlinkDurationMin,ledBlinkDurationMax);
}

void piezoLoop ()
{
  tone (piezoPIN, pitch, duration);
}

float toDegreesCelsius (int value)
{
  float voltage = value * 5.0;
  voltage /= 1024.0;
  return (voltage - 0.5) * 100;
}
