//////////////////////////////     Import des librairies     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  

#include <ESP8266WiFi.h>
#include <FastLED.h>

//////////////////////////////     Config du  reseau     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

IPAddress local_IP(192, 168, 0, 11);                                         // IP fixe ruban1
IPAddress gateway(192, 168, 0, 254);                                         // Passerelle
IPAddress subnet(255, 255, 255, 0);                                          // Masque

WiFiServer serveur(80);                                                      // Serveur TCP

const char* ssid = "RubanLedWIFI";                                           // SSID
const char* password = "123456789";                                          // Mdp SSID

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

#define SON_PIN     16                                                       // Pin D0 gpio16 le module son

//////////////////////////////     Config du  StripWS8211     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

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

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 pour associer des couleurs au leds. 
uint8_t gHue = 0;                                                             // Variable pour le choix de la couleur

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

CRGBPalette16 mer = mer_gp;                                                   // association de la palette mer
                                                                    

const TProgmemPalette16 BleuBlancRouge_p PROGMEM = {                          // Création d'un pallette BleuBlancRouge
  CRGB::Red, CRGB::Gray, CRGB::Blue, CRGB::Black,                             //     (Stocké dans la mémoire flash) 
  CRGB::Red, CRGB::Gray, CRGB::Blue, CRGB::Black,  
  CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
  CRGB::Blue, CRGB::Blue, CRGB::Black, CRGB::Black  
};
    
extern CRGBPalette16 BleuBlancRouge;                                          // Association de la palette BleuBlancRouge
extern const TProgmemPalette16 BleuBlancRouge_p PROGMEM;                      // Déclaration de la palette BleuBlancRouge

void VioletNoirVertPalette() {                                           // fonction qui crée la palette vert violet noir
  CRGB purple = CHSV( HUE_PURPLE, 255, 255);                                //      (Stocké dans le programme)  
  CRGB green  = CHSV( HUE_GREEN, 255, 255);
  CRGB black  = CRGB::Black;
  
  currentPalette = CRGBPalette16(
    green,  green,  black,  black,
    purple, purple, black,  black,
    green,  green,  black,  black,
    purple, purple, black,  black );
}

void VertJauneRougePalette() {                                     // fonction qui crée la palette vert jaune rouge
  CRGB vert = CHSV( HUE_GREEN, 255, 255);                                    //      (Stocké dans le programme)  
  CRGB jaune  = CHSV( HUE_YELLOW, 255, 255);
  CRGB rouge  = CHSV( HUE_RED, 255, 255);
  CRGB noir  = CRGB::Black;
  
  currentPalette = CRGBPalette16( 
    vert,  jaune,  rouge,  noir,
    vert,  jaune,  rouge,  noir,
    vert,  jaune,  rouge,  noir,
    vert,  jaune,  rouge,  noir );    
}

//////////////////////////////     Fonctions qui fabrique des effets avec différentes palettes    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

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 = PartyColors_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(50);
  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);    
}

//////////////////////////////     Le setup     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void setup() {
  delay( 3000 );                                  // delay pour démarage
  Serial.begin(115200); 
  pinMode(SON_PIN, INPUT);
  Serial.println();                          
  WiFi.config(local_IP, gateway, subnet);         // on contruit l'ip fixe
  WiFi.begin(ssid, password);                     // on se connecte au réseau WiFi
  while (WiFi.status() != WL_CONNECTED) {          
    Serial.print(".");
    delay(500);
  }
  Serial.print("Connection à ");
  Serial.print("SSID: "); 
  Serial.println(WiFi.SSID());
  Serial.print("IP: ");     
  Serial.println(WiFi.localIP());
  serveur.begin();                                // On démarre le serveur
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); // On configure le ruban
  FastLED.setBrightness(  BRIGHTNESS );               
  fill_solid( currentPalette, 16, CRGB::Black);   // On met toutes les leds en noir
  FastLED.show();                                 // On affiche
  for (int i = 0; i < NUM_LEDS; i++) {            // On rempli le tableau colorIndex  
    colorIndex[i] = random8();
  }
  Serial.println("setup(): OK");                  // Fin du setup
}

//////////////////////////////     Le Loop     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void loop() {  
  recupdata();      
  ChangeStyle();     
}

//////////////////////////////     La fonction qui récupère la commande du maitre     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void recupdata() {
  WiFiClient client = serveur.available();              // Déclaration du client
  if (client) {                                         // Si le serveur est opérationel
    if (client.connected()) {                           // Si un client se connecte
      String message = client.readStringUntil('\r');    // On récuppère le message du client jusqu'a \r
      commande = message.toInt();                       // On transforme le sting en int et on l'associe a la variable commande
      Serial.print("message: ");                        // On affiche le message
      Serial.println(message);                          
      Serial.print("commande: ");                       // On affiche la commande
      Serial.println(commande);        
      client.flush();                                   // On vide le buffer
      client.stop();                                    // On coupe la connection du client
    }
  }
}

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

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

//////////////////////////////     La fonction qui change la luminosité en fonction du son     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void flashSon() {          
  // Si le son dépasse le seuil (vaut 1) alors la luninosité vaut 200 sinon valeur vaut BRIGHTNESS (BRIGHTNESS = 20)
  digitalRead(SON_PIN) ? FastLED.setBrightness(200) : FastLED.setBrightness( BRIGHTNESS ); 
}  

//////////////////////////////     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 = LINEARBLEND; }
    if( commande == 2 ) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }    
    if( commande == 3 ) { VioletNoirVertPalette(); currentBlending = LINEARBLEND; }
    if( commande == 4 ) { VertJauneRougePalette(); 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 <= 11) {
    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++; }
  } 
  flashSon();
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
}