//////////////////////////////    les librairies     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <FastLED.h>

//////////////////////////////    Le debug     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#define DEBUG true                                  // active ou pas le serial
#define Serial if(DEBUG)Serial

//////////////////////////////    Le WIFI     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

const char* host = "RubanWeb";                      // le nom net bios
const char* ssid = "XXXXXXX";                       // votre SSID
const char* password = "XXXXXXXXXXXXXXXXXX";        // le mdp du SSID 
ESP8266WebServer httpServer(80);

//////////////////////////////    config page update     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

const char* update_username = "YYYY";               // le login de la fonction update
const char* update_password = "YYYYYYYYY";          // le mdp de la fonction update
const char* update_path = "/update";                // la page de la fonction update
ESP8266HTTPUpdateServer httpUpdater;

//////////////////////////////     Config du  StripWS8212B     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#define LED_PIN     3                                                         // Pin D3 gpio0
#define NUM_LEDS    144                                                       // Nb de leds sur le ruban
#define BRIGHTNESS  50                                                        // Luminosité (0 à 255)
#define LED_TYPE    WS2812B                                                   // Type de led
#define COLOR_ORDER GRB                                                       // Ordre des couleurs
#define UPDATES_PER_SECOND 100                                                // Pour le delay des leds


int commande = 0;                                                            // Variable pour stocker la commande à envoyer 

CRGBArray<NUM_LEDS> leds;                                                     // Construction de la matrice de led/couleur
CRGBPalette16 currentPalette;                                                 // Variable currentPalette pour stocker des palettes.
TBlendType    currentBlending;                                                // Variable currentBlending pour stocker des effets. 

uint8_t colorIndex[NUM_LEDS];                                                 // Tableau de position des leds. 
uint8_t gHue = 0;                                                             // Variable pour le choix de la couleur

extern CRGBPalette16 BleuBlancRouge;                                          // Association de la palette BleuBlancRouge
extern const TProgmemPalette16 BleuBlancRouge_p PROGMEM;                      // Stocké dans la mémoire flash 

extern CRGBPalette16 VertJauneRouge;                                          // Association de la palette BleuBlancRouge
extern const TProgmemPalette16 VertJauneRouge_p PROGMEM;                      // Stocké dans la mémoire flash 

extern CRGBPalette16 VioletNoirVert;                                          // Association de la palette BleuBlancRouge
extern const TProgmemPalette16 VioletNoirVert_p PROGMEM;                      // Stocké dans la mémoire flash 

extern CRGBPalette16 VertJauneRouge;                                          // Association de la palette BleuBlancRouge
extern const TProgmemPalette16 VertJauneRouge_p PROGMEM;                      // Stocké dans la mémoire flash 

const TProgmemPalette16 BleuBlancRouge_p PROGMEM = {                          // Création d'un pallette BleuBlancRouge
  CRGB::Red, CRGB::Gray, CRGB::Blue, CRGB::Black,                             
  CRGB::Red, CRGB::Gray, CRGB::Blue, CRGB::Black,  
  CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
  CRGB::Blue, CRGB::Blue, CRGB::Black, CRGB::Black  
};

const TProgmemPalette16 VioletNoirVert_p PROGMEM = {                          // Création d'une palette VioletNoirVert
  CRGB::Purple, CRGB::Purple, CRGB::Black, CRGB::Black,                             
  CRGB::Green, CRGB::Green, CRGB::Black, CRGB::Black,  
  CRGB::Purple, CRGB::Purple, CRGB::Black, CRGB::Black,                             
  CRGB::Green, CRGB::Green, CRGB::Black, CRGB::Black                                                                              
};

const TProgmemPalette16 VertJauneRouge_p PROGMEM = {                          // Création d'une palette VertJauneRouge
  CRGB::Green, CRGB::Yellow, CRGB::Red, CRGB::Black,                             
  CRGB::Green, CRGB::Yellow, CRGB::Red, CRGB::Black,  
  CRGB::Green, CRGB::Yellow, CRGB::Red, CRGB::Black,                             
  CRGB::Green, CRGB::Yellow, CRGB::Red, CRGB::Black                                                                              
};

DEFINE_GRADIENT_PALETTE( mer_gp ) {                                            // Création d'une palette "mer" autour du bleu
  0,   0,  255, 245,
  46,  0,  21,  255,
  179, 12, 250, 0,
  255, 0,  255, 245
};
CRGBPalette16 mer = mer_gp;      

//////////////////////////////     Le SETUP     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void setup(){ 
  Serial.begin(115200);  
  delay(1000);
  Serial.println();  
  Serial.println("initialisation");                       // infos
  Serial.print("Connexion à ");                           
  Serial.print(ssid);  
  WiFi.begin(ssid, password);                             // connexion au réseau
  Serial.println("");
  int h = 0;                                              
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");    
    if (h++ > 40) { // si trop long on abandonne
      Serial.println();
      Serial.println("Échec de connexion");
      return;
    }    
  }
  Serial.println();                                       // infos de connexion au réseau
  Serial.print("Connexion OK à ");
  Serial.println(ssid);
  Serial.print("Adresse IP: ");
  Serial.println(WiFi.localIP());    
  httpServer.on("/", handle_root);                        // chaque fonction est associée à un lien
  httpServer.on("/0", handle_off);
  httpServer.on("/1", handle_RainbowColors);
  httpServer.on("/2", handle_RainbowStripeColors);
  httpServer.on("/3", handle_VertJauneRougePalette);
  httpServer.on("/4", handle_VioletNoirVertPalette);   
  httpServer.on("/5", handle_BleuBlancRouge);
  httpServer.on("/6", handle_sinelon);
  httpServer.on("/7", handle_bpm);
  httpServer.on("/8", handle_confetti);
  httpServer.on("/9", handle_juggle);
  httpServer.on("/10", handle_vague);
  httpServer.on("/11", handle_DoubleSens);
  httpServer.on("/12", handle_Pride);
  MDNS.begin(host);                                                                                 // On démarre le service MDNS
  httpUpdater.setup(&httpServer, update_path, update_username, update_password);                    // On configure l'accès à la page update  
  httpServer.begin();                                                                               // On démarre le serveur web 
  MDNS.addService("http", "tcp", 80);                                                               // On construit le lien net bios
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); // On configure le ruban
  FastLED.setBrightness(  BRIGHTNESS );                                                             // On règle la luminosité
  fill_solid( currentPalette, 16, CRGB::Black);                                                     // On met toutes les leds en noir
  FastLED.show();                                                                                   // On valide
  for (int i = 0; i < NUM_LEDS; i++) {                                                              // On rempli le tableau colorIndex  
    colorIndex[i] = random8();
  }  
  Serial.printf("Ouvre http://%s.local ou http://", host);                                          // infos de connexion à la page web
  Serial.print(WiFi.localIP());
  Serial.println(" dans ton navigateur pour me commander"); 
  Serial.println("initialisation OK");                                                              // Fin du setup
}

//////////////////////////////     Le LOOP     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void loop(){ 
  MDNS.update();                 // pour maintenir le nom net bios
  httpServer.handleClient();     // on écoute si un client se connecte en boucle
  ChangeStyle();                 // la fonction ChangeStyle 
}

//////////////////////////////     La fonction qui rempli le ruban avec certaines palettes prédéfinies     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void FillLEDsFromPaletteColors( uint8_t colorIndex) {     
  for( int i = 0; i < NUM_LEDS; ++i) {
      leds[i] = ColorFromPalette( currentPalette, colorIndex, BRIGHTNESS, currentBlending);
      colorIndex += 3;
  }
}

//////////////////////////////     Fonctions qui fabrique des effets      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void pride() {
  static uint16_t sPseudotime = 0;
  static uint16_t sLastMillis = 0;
  static uint16_t sHue16 = 0; 
  uint8_t sat8 = beatsin88( 87, 220, 250);
  uint8_t brightdepth = beatsin88( 341, 96, 224);
  uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
  uint8_t msmultiplier = beatsin88(147, 23, 60);
  uint16_t hue16 = sHue16;//gHue * 256;
  uint16_t hueinc16 = beatsin88(113, 1, 3000);  
  uint16_t ms = millis();
  uint16_t deltams = ms - sLastMillis ;
  sLastMillis  = ms;
  sPseudotime += deltams * msmultiplier;
  sHue16 += deltams * beatsin88( 400, 5,9);
  uint16_t brightnesstheta16 = sPseudotime;  
  for( uint16_t i = 0 ; i < NUM_LEDS; i++) {
    hue16 += hueinc16;
    uint8_t hue8 = hue16 / 256;
    brightnesstheta16  += brightnessthetainc16;
    uint16_t b16 = sin16( brightnesstheta16  ) + 32768;
    uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
    uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
    bri8 += (255 - brightdepth);    
    CRGB newcolor = CHSV( hue8, sat8, bri8);    
    uint16_t pixelnumber = i;
    pixelnumber = (NUM_LEDS-1) - pixelnumber;    
    nblend( leds[pixelnumber], newcolor, 64);
  }
}

void vague() {    
  uint8_t sinBeat = beatsin8(30, 50, 255, 0, 0);
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette(mer, colorIndex[i], sinBeat);
  }      
  EVERY_N_MILLISECONDS(5){
    for (int i = 0; i < NUM_LEDS; i++) {
      colorIndex[i]++;
    }
  }
}

void sinelon() {
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16( 13, 0, NUM_LEDS-1 );
  leds[pos] += CHSV( gHue, 255, 192);
}

void bpm() {
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = VertJauneRouge_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { 
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

void juggle() {
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}

void confetti() {
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}

void DoubleSens() {
  leds.fadeToBlackBy(10);
  int pos1 = beatsin16( 13, 0, NUM_LEDS/2-1 );  
  int pos2 = beatsin16( 13, NUM_LEDS/2, NUM_LEDS-1 );
  leds[pos1] += CHSV( gHue, 255, 192);
  leds[(NUM_LEDS-1)-pos1] += CHSV( gHue, 255, 192);
}

void SetupOffPalette() {
  fill_solid( currentPalette, 16, CRGB::Black);    
}

//////////////////////////////     La fonction qui change le style du ruban en fonction de la commande reçu     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void ChangeStyle() {         
  if  (commande >= 0 and commande <= 5) {
    if( commande == 0 ) { SetupOffPalette(); }
    if( commande == 1 ) { currentPalette = RainbowColors_p; currentBlending = NOBLEND; } // LINEARBLEND
    if( commande == 2 ) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }    
    if( commande == 3 ) { currentPalette = VioletNoirVert_p; currentBlending = NOBLEND; }
    if( commande == 4 ) { currentPalette = VertJauneRouge_p; currentBlending = NOBLEND; }
    if( commande == 5 ) { currentPalette = BleuBlancRouge_p; currentBlending = NOBLEND; }    
    static uint8_t startIndex = 0;
    startIndex = startIndex + 1;    
    FillLEDsFromPaletteColors( startIndex);    
  }
  if  (commande >= 6 and commande <= 12) {
    if( commande == 6)  { sinelon(); }
    if( commande == 7)  { bpm(); } 
    if( commande == 8)  { confetti(); }
    if( commande == 9)  { juggle(); }
    if( commande == 10)  { vague(); }    
    if( commande == 11)  { DoubleSens(); }           
    EVERY_N_MILLISECONDS( 20 ) { gHue++; }
    if( commande == 12)  { pride(); }
  }  
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);  
}

//////////////////////////////     La page web   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

// le HTML

void handle_root() {
  char page[] = "<body style='background:#7F7F7F;'>"               
  "<center><h2 style='color:#EC7878;'>La fonction Update</h2></center><br/>"
  "<center><p><a href=\"update\"><button style='background:#EC7878;border:2px solid black;font-size: 20px;padding: 20px 20px;border-radius: 25px;'>update du programme</button></a></p></center><br/>"  
  "<center><h2>Commande du ruban</h2></center><br/>" 
  "<center><div>"
  "<p><a href=\"1\"><button>RainbowColors</button></a> &nbsp; &nbsp; <a href=\"2\"><button>RainbowStripeColors</button></a> &nbsp; &nbsp;"  
  "<a href=\"3\"><button>VertJauneRouge</button></a> &nbsp; &nbsp; <a href=\"4\"><button>VioletNoirVert</button></a> &nbsp; &nbsp;"
  "<a href=\"5\"><button>BleuBlancRouge</button></a> &nbsp; &nbsp; <a href=\"6\"><button>sinelon</button></a> &nbsp; &nbsp;"
  "<a href=\"7\"><button>bpm</button></a></p>"  
  "<p><a href=\"8\"><button>confetti</button></a> &nbsp; &nbsp; <a href=\"9\"><button>juggle</button></a> &nbsp; &nbsp;"
  "<a href=\"10\"><button>vague</button></a> &nbsp; &nbsp; <a href=\"11\"><button>DoubleSens</button></a> &nbsp; &nbsp; <a href=\"12\"><button>Pride</button></a></p>"
  "<p><a href=\"0\"><button>OFF</button></a></p>"
  "</center></div>"  
  "</body>";                
  httpServer.send(200, "text/html", page); // repond avec la page web codee en HTML
}

// les fonctions associées aux boutons et onglets

void handle_off() { commande = 0; handle_root(); }
void handle_RainbowColors() { commande = 1; handle_root(); }
void handle_RainbowStripeColors() { commande = 2; handle_root(); }
void handle_VioletNoirVertPalette() { commande = 3; handle_root(); }
void handle_VertJauneRougePalette() { commande = 4; handle_root(); }
void handle_BleuBlancRouge() { commande = 5; handle_root(); }
void handle_sinelon() { commande = 6; handle_root(); }
void handle_bpm() { commande = 7; handle_root(); }
void handle_confetti() { commande = 8; handle_root(); }
void handle_juggle() { commande = 9; handle_root(); }
void handle_vague() { commande = 10; handle_root(); }
void handle_DoubleSens() { commande = 11; handle_root(); }
void handle_Pride() { commande = 12; handle_root(); }