/* ESP32 HTTP IoT Server Example for Wokwi.com

  https://wokwi.com/projects/320964045035274834

  To test, you need the Wokwi IoT Gateway, as explained here:

  https://docs.wokwi.com/guides/esp32-wifi#the-private-gateway

  Then start the simulation, and open http://localhost:9080
  in another browser tab.

  Note that the IoT Gateway requires a Wokwi Club subscription.
  To purchase a Wokwi Club subscription, go to https://wokwi.com/club
*/

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <uri/UriBraces.h>

#include <FastLED.h>
#include <EEPROM.h>
#include <SoftwareSerial.h>

const byte rxPin = 3;
const byte txPin = 4;

SoftwareSerial mySerial (rxPin, txPin);
byte serialMode;

#define NUM_LEDS 11
CRGB leds[NUM_LEDS];
#define PIN 27

bool firstRun;
bool flipFlop;
byte i;
byte mode;
byte maxMode = 4;
float rampSpeed;

unsigned long startTime;
unsigned long updateFrequency;
unsigned long currentTime;
byte brightness = 100;

const byte flake7 = 0;
const byte flake6 = 1;
const byte l = 2;
const byte flake5 = 3;
const byte e = 4;
const byte flake4 = 5;
const byte o = 6;
const byte flake3 = 7;
const byte n = 8;
const byte flake2 = 9;
const byte flake1 = 10;

const byte group1start = 74;
const byte group1end = 76;
const byte group2start = 77;
const byte group2end = 78;
const byte group3start = 79;
const byte group3end = 80;
const byte group4start = 81;
const byte group4end = 82;
const byte group5start = 83;
const byte group5end = 85;
const byte group6start = 67;
const byte group6end = 73;
const byte group7start = 59;
const byte group7end = 65;
const byte group8start = 52;
const byte group8end = 57;
const byte gruop9start = 48;
const byte group9end = 49;
const byte group10start = 40;
const byte group10end = 47;
const byte group11start = 32;
const byte group11end = 37;

void setPixel(int Pixel, float red, float green, float blue) {
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
}

void rampSingle(float red, float green, float blue) {
  for(float r = 0.01; r < 1; r + 0.01) {
    red = red * r;
    green = green * r;
    blue = blue * r;
    delay(rampSpeed);
  }
}

void setAll(float red, float green, float blue) {
  //for(float r = 0.01; r < 1; r + 0.01) {
    //red = red * r;
    //green = green * r;
    //blue = blue * r;
    for(int i = 0; i < NUM_LEDS; i++) {
      setPixel(i, red, green, blue);
    }
  setSkippedLedsOff();
  FastLED.show();
  //delay(rampSpeed);
}

void setSkippedLedsOff() {
  setPixel(7, 0, 0, 0);
  setPixel(14, 0, 0, 0);
  setPixel(15, 0, 0, 0);
  setPixel(16, 0, 0, 0);
  setPixel(23, 0, 0, 0);
  setPixel(31, 0, 0, 0);
  setPixel(38, 0, 0, 0);
  setPixel(39, 0, 0, 0);
  setPixel(50, 0, 0, 0);
  setPixel(51, 0, 0, 0);
  setPixel(58, 0, 0, 0);
  setPixel(66, 0, 0, 0);
}

void setAllLetters(byte red, byte green, byte blue) {
  setLetters(m1, red, green, blue);
  setLetters(e, red, green, blue);
  setLetters(r1, red, green, blue);
  setLetters(r2, red, green, blue);
  setLetters(y, red, green, blue);
  setLetters(c, red, green, blue);
  setLetters(h, red, green, blue);
  setLetters(r3, red, green, blue);
  setLetters(s1, red, green, blue);
  setLetters(t, red, green, blue);
  setLetters(m2, red, green, blue);
  setLetters(a, red, green, blue);
  setLetters(s2, red, green, blue);
  FastLED.show();
}

void setMerry(byte red, byte green, byte blue) {
  setLetters(m1, red, green, blue);
  setLetters(e, red, green, blue);
  setLetters(r1, red, green, blue);
  setLetters(r2, red, green, blue);
  setLetters(y, red, green, blue);
  FastLED.show();
}

void setChrstmas(byte red, byte green, byte blue) {
  setLetters(c, red, green, blue);
  setLetters(h, red, green, blue);
  setLetters(r3, red, green, blue);
  setLetters(s1, red, green, blue);
  setLetters(t, red, green, blue);
  setLetters(m2, red, green, blue);
  setLetters(a, red, green, blue);
  setLetters(s2, red, green, blue);
  FastLED.show();
}

void setChristmas(byte red, byte green, byte blue) {
  setLetters(c, red, green, blue);
  setLetters(h, red, green, blue);
  setLetters(r3, red, green, blue);
  setLetters(star, red, green, blue);
  setLetters(tree, red, green, blue);
  setLetters(s1, red, green, blue);
  setLetters(t, red, green, blue);
  setLetters(m2, red, green, blue);
  setLetters(a, red, green, blue);
  setLetters(s2, red, green, blue);
  FastLED.show();
}

void setMerryAlternate(byte red1, byte green1, byte blue1, byte red2, byte green2, byte blue2) {
  setLetters(m1, red1, green1, blue1);
  setLetters(r1, red1, green1, blue1);
  setLetters(y, red1, green1, blue1);
  setLetters(e, red2, green2, blue2);
  setLetters(r2, red2, green2, blue2);
  FastLED.show();
}

void setChristmasAlternate(byte red1, byte green1, byte blue1, byte red2, byte green2, byte blue2) {
  setLetters(c, red1, green1, blue1);
  setLetters(r3, red1, green1, blue1);
  setLetters(s1, red1, green1, blue1);
  setLetters(m2, red1, green1, blue1);
  setLetters(s2, red1, green1, blue1);
  setLetters(h, red2, green2, blue2);
  setLetters(star, red2, green2, blue2);
  setLetters(tree, red2, green2, blue2);
  setLetters(t, red2, green2, blue2);
  setLetters(a, red2, green2, blue2);
  FastLED.show();
}

void setTreeNormal() {
  setLetters(star, 255, 255, 0);
  setLetters(tree, 0, 255, 0);
  FastLED.show();
}


void setLetters(byte letter, byte red, byte green, byte blue) {
  byte ledStart;
  byte ledEnd;
  switch (letter) {
    case 0:
      ledStart = group1start;
      ledEnd = group1end;
      break;
    case 1:
      ledStart = group2start;
      ledEnd = group2end;
      break;
    case 2:
      ledStart = group3start;
      ledEnd = group3end;
      break;
    case 3:
      ledStart = group4start;
      ledEnd = group4end;
      break;
    case 4:
      ledStart = group5start;
      ledEnd = group5end;
      break;
    case 5:
      ledStart = group6start;
      ledEnd = group6end;
      break;
    case 6:
      ledStart = group7start;
      ledEnd = group7end;
      break;
    case 7:
      ledStart = group8start;
      ledEnd = group8end;
      break;
    case 8:
      ledStart = gruop9start;
      ledEnd = group9end;
      break;
    case 9:
      ledStart = group10start;
      ledEnd = group10end;
      break;
    case 10:
      ledStart = group11start;
      ledEnd = group11end;
      break;
    case 11:
      ledStart = tStart;
      ledEnd = tEnd;
      break;
    case 12:
      ledStart = m2Start;
      ledEnd = m2End;
      break;
    case 13:
      ledStart = aStart;
      ledEnd = aEnd;
      break;
    case 14:
      ledStart = s2Start;
      ledEnd = s2End;
      break;
  }
  for(int i = ledStart; i <= ledEnd; i++) {
    float redDim = red / brightness; float grgroup2endim = green / brightness; float blueDim = blue / brightness;
    setPixel(i, red, green, blue);
  }
  //FastLED.show();
}

void saveModeToEEPROM() {
  EEPROM.write(0, mode);
}

void serialIncoming() {
  firstRun = true;
  startTime = startTime - 5000;
  serialMode = mySerial.read();
  Serial.print("Mode Set by Serial is "); Serial.println(serialMode);
  mode = serialMode;
  saveModeToEEPROM();
}

void modeChange() {
  firstRun = true;
  startTime = startTime - 5000;
  mode++;
  if(mode > maxMode) {
    mode = 0;
  }
  saveModeToEEPROM();
}

void buttonPress() {
  modeChange();
}

#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// Defining the WiFi channel speeds up the connection:
#define WIFI_CHANNEL 6

WebServer server(80);

const int LED1 = 26;
const int LED2 = 27;

bool led1State = false;
bool led2State = false;

void sendHtml() {
  String response = R"(
    <!DOCTYPE html><html>
      <head>
        <title>ESP32 Web Server Demo</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
          html { font-family: sans-serif; text-align: center; }
          body { display: inline-flex; flex-direction: column; }
          h1 { margin-bottom: 1.2em; } 
          h2 { margin: 0; }
          div { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; grid-auto-flow: column; grid-gap: 1em; }
          .btn { background-color: #5B5; border: none; color: #fff; padding: 0.5em 1em;
                 font-size: 2em; text-decoration: none }
          .btn.OFF { background-color: #333; }
        </style>
      </head>
            
      <body>
        <h1>ESP32 Web Server</h1>

        <div>
          <h2>LED 1</h2>
          <a href="/toggle/1" class="btn LED1_TEXT">LED1_TEXT</a>
          <h2>LED 2</h2>
          <a href="/toggle/2" class="btn LED2_TEXT">LED2_TEXT</a>
        </div>
      </body>
    </html>
  )";
  response.replace("LED1_TEXT", led1State ? "ON" : "OFF");
  response.replace("LED2_TEXT", led2State ? "ON" : "OFF");
  server.send(200, "text/html", response);
}

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

  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  setAll(0, 0, 0);
  mode = EEPROM.read(0);

  pinMode(button, INPUT_PULLUP);

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  mySerial.begin(9600);


  startTime = millis();
  currentTime = millis();
  updateFrequency = 1;
  Serial.begin(9600);
  Serial.println("Hello World");

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
  Serial.print("Connecting to WiFi ");
  Serial.print(WIFI_SSID);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" Connected!");

  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", sendHtml);

  server.on(UriBraces("/toggle/{}"), []() {
    String led = server.pathArg(0);
    Serial.print("Toggle LED #");
    Serial.println(led);

    switch (led.toInt()) {
      case 1:
        led1State = !led1State;
        digitalWrite(LED1, led1State);
        break;
      case 2:
        led2State = !led2State;
        digitalWrite(LED2, led2State);
        break;
    }

    sendHtml();
  });

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();

  byte buttonState = digitalRead(button);
  if(buttonState == LOW) {
    Serial.println("Button Pressed");
    delay(150);
    buttonPress();
  }

  if(mySerial.available() > 0) {
    serialIncoming();
  }

  currentTime = millis();  
  if(updateFrequency <= currentTime - startTime) {
    startTime = millis();
    Serial.println("Timer Updated");
    switch(mode) {
      case 10: // All Off
        updateFrequency = 2000;
        setAll(0, 0, 0);
        Serial.println("Mode 10");
        break;
      case 0: // All White
        updateFrequency = 2000;
        setAll(255, 255, 255);
        Serial.println("Mode 0");
        break;
      case 1: // All White & Tree Colors
        updateFrequency = 5000;
        setAllLetters(255, 255, 255);
        setTreeNormal();
        Serial.println("Mode 1");
        break;
      case 2: // Candy Cane
        updateFrequency = 2000;
        setMerryAlternate(255, 0, 0, 255, 255, 255);
        setChristmasAlternate(255, 0, 0, 255, 255, 255);
        Serial.println("Mode 2");
        break;
      case 3: // Candy Cane Dance
        if(firstRun == true) {
          firstRun = false;
          updateFrequency = 1000;
          flipFlop = true;
        }
        Serial.println("Mode 3");
        if(flipFlop == true) {
          flipFlop = false;
          setMerryAlternate(255, 0, 0, 255, 255, 255);
          setChristmasAlternate(255, 0, 0, 255, 255, 255);
        }
        else {
          flipFlop = true;
          setMerryAlternate(255, 255, 255, 255, 0, 0);
          setChristmasAlternate(255, 255, 255, 255, 0, 0);
        }
        break;
      case 4: // One by One White
        if(firstRun == true) {
          firstRun = false;
          updateFrequency = 500;
          setAll(0, 0, 0);
          i = 0;
        }
        Serial.println("Mode 4");
        if(i <= 7 || (i >= 10 && i <= 14)) {
          setLetters(i, 255, 255, 255);
          FastLED.show();
        }
        if(i == 8) {
          setLetters(i, 255, 255, 0);
          FastLED.show();
        }
        if(i == 9) {
          setLetters(i, 0, 255, 0);
          FastLED.show();
        }
        if(i > 14) {
          switch(i) {
            case 15:
              setAll(0, 0, 0);
              break;
            case 16:
              setAllLetters(255, 255, 255);
              setTreeNormal();
              break;
            case 24:
              firstRun = true;
              setAll(0, 0, 0);
              break;
          }
        }
        i++;
        break;
      case 5: // One by One Candy Cane
        if(firstRun == true) {
          firstRun = false;
          updateFrequency = 500;
          setAll(0, 0, 0);
          i = 0;
        }
        Serial.println("Mode 5");
        if(i <= 4) {
          if((i % 2) == 0) {
            setLetters(i, 255, 255, 255);
          }
          else {
            setLetters(i, 255, 0, 0);
          }
          FastLED.show();
        }
        if(i > 4 && i < 8) {
          if((i % 2) == 0) {
            setLetters(i, 255, 0, 0);
          }
          else {
            setLetters(i, 255, 255, 255);
          }
          FastLED.show();
        }
        if(i == 8) {
          setLetters(i, 255, 255, 255);
          i++;
          setLetters(i, 255, 255, 255);
          FastLED.show();
        }
        if(i > 9 && i < 15) {
          if((i % 2) == 0) {
            setLetters(i, 255, 0, 0);
          }
          else {
            setLetters(i, 255, 255, 255);
          }
          FastLED.show();
        }
        if(i > 14) {
          switch(i) {
            case 15:
              setAll(0, 0, 0);
              break;
            case 16:
              setMerryAlternate(255, 0, 0, 255, 255, 255);
              setChristmasAlternate(255, 0, 0, 255, 255, 255);
              break;
            case 24:
              firstRun = true;
              setAll(0, 0, 0);
              break;
          }
        }
        i++;
        break;
    }
  }
}