#include <Adafruit_NeoPixel.h>
#include "RTClib.h"

RTC_DS3231 rtc;


/**********/
/* CONFIG */
/**********/

// heure de début de l'animation
int start_hour = 17;

// heure de fin de l'animation
int end_hour = 23;

// durée de chaque effet (en millisecondes)
int effect_duration = 180000; // 3 minutes

/**************/
/* FIN CONFIG */
/**************/



#define LED_PIN 2 // PIN du signal vers le LEDs
#define LED_COUNT 80 // nombre de LEDs dans le circuit

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

bool h12 = false; // Utile pour récupérer l'heure
bool PM; // Utile pour récupérer l'heure
bool played = false; // animation déjà jouée ?
int i_func = 0; // ID de l'effet à jouer
static unsigned long lastTimeItHappened = 0; // variable pour l'enchainement des effets
int current_hour; // heure de lancement du script
bool effect_every_hour = false; // utilisé pour savoir si l'effet lancé toutes les heures à été déclanché

// liste des effets
typedef void (*FuncPtr)(void);
FuncPtr myFunctions[] = { &effect_rainbow, &effect_double_rotate, &effect_tricolors, &effect_heart, &effect_6_colors };

// config des couleurs pour l'effet "effect_6_colors"
int a_red_6_colors[] = {255, 0, 0, 255, 0, 255};
int a_green_6_colors[] = {0, 255, 0, 255, 255, 0};
int a_blue_6_colors[] = {0, 0, 255, 0, 255, 255};
int colors_offset = 0;

int l = 0;
int color_r;
int color_v;
int color_b;


void setup() {

  Serial.begin(57600);

  // reset des LEDs
  strip.begin();
  strip.setPixelColor(0, 0, 0, 0);
  strip.show();
  
  rtc.begin();

  // met à l'heure le module RTC si besoin
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // récup. l'heure de lancement du script
  current_hour = get_hours();
  Serial.print(current_hour);
  Serial.print(':');
  Serial.println(get_minutes());

  delay(100);
  
}

int get_hours() {
  DateTime now = rtc.now();
  return now.hour();
}

int get_minutes() {
  DateTime now = rtc.now();
  return now.minute();
}

void loop() {

  // si on est dans le créneau horaire d'activation de l'animation
  if (get_hours() >= start_hour && get_hours() < end_hour) {

    // on indique que l'animation a été joué
    played = true;

    // si on vient de changer d'heure, on lance l'effect EIFFEL TOWER
    int new_hour = get_hours();
    if (current_hour != new_hour) {
      current_hour = new_hour;
      effect_every_hour = true;
    }

    // lancement de l'effet EIFFEL TOWER pendant 1 minute
    if (effect_every_hour) {
      effect_eiffel_tower();
      if (get_minutes() >= 1) {
        effect_every_hour = false;
        strip.clear();
      }
    }

    // sinon, lancement des effets en boucle
    else {

      // si l'effet actuel a été joué pendant au moins "effect_duration" millisecondes, on passe à l'effet suivant
      if (millis() - lastTimeItHappened >= effect_duration) {
        lastTimeItHappened = millis();
        i_func++;
        strip.clear();
        if (i_func == 5) {
          i_func = 0;
        }
      }

      // lancement de l'effet
      myFunctions[i_func]();
      
    }
  }

  // on n'est pas dans le créneau horaire
  else {

    // si l'animation a déjà été jouée
    if (played) {

      // on éteind tout
      played = false;
      strip.clear();
      strip.show();
      
    }
  }
}



// EFFET RAINBOW
void effect_rainbow() {
  rainbow(30);
  delay(10);
}



// EFFET 6 COULEURS
void effect_6_colors() {

  if (colors_offset > 5) colors_offset = 0;
  int j = 0 + colors_offset;
  for (int i = 0; i < LED_COUNT; i += 4) {
      if (j > 5) j = 0;
      int r = a_red_6_colors[j];
      int v = a_green_6_colors[j];
      int b = a_blue_6_colors[j];
      strip.setPixelColor(i, r, v, b);
      strip.setPixelColor(i + 1, r, v, b);
      strip.setPixelColor(i + 2, r, v, b);
      strip.setPixelColor(i + 3, r, v, b);
      j++;
  }
  
  strip.show();
  delay(1000);
  colors_offset++;
  
}



// EFFET DOUBLE ROTATION
void effect_double_rotate() {

  for (int i = 0; i < LED_COUNT / 2 ; i += 4) {
    strip.setPixelColor(i, 255, 0, 0);
    strip.setPixelColor(i + 1, 255, 0, 0);
    strip.setPixelColor(i + 2, 255, 0, 0);
    strip.setPixelColor(i + 3, 255, 0, 0);
    strip.setPixelColor(i + (LED_COUNT / 2), 255, 0, 0);
    strip.setPixelColor(i + (LED_COUNT / 2) + 1, 255, 0, 0);
    strip.setPixelColor(i + (LED_COUNT / 2) + 2, 255, 0, 0);
    strip.setPixelColor(i + (LED_COUNT / 2) + 3, 255, 0, 0);
    strip.show();
    delay(100);
    strip.clear();
    strip.show();
  }
}



// EFFET TRICOLORS
void effect_tricolors() {

  int offset1 = 3;
  int offset2 = 4;
  int r1 = 0;

  for (int led = r1; led <= LED_COUNT; led += offset2) {
    strip.setPixelColor(led, 0, 0, 255);
    strip.setPixelColor(led + offset1, 255, 0, 0);
  }
  
  strip.show();
  
}



// EFFET COEUR QUI BAT
void effect_heart() {

  int offset = 2;
  int r1 = 2;
  int r2 = r1 - offset;
  int plus1 = 25;
  int plus2 = 20;
  int delay1 = 10;
  int delay2 = 50;
  int delay3 = 1000;
  int idle = 20;

  for (int i = idle; i <= 255; i += plus1) {
    for (int led = r1; led <= LED_COUNT; led += 4) {
      strip.setPixelColor(led, i, 0, 0);
      strip.setPixelColor(led + 1, i, 0, 0);
    }
    strip.show();
    delay(delay1);
  }

  for (int i = idle; i <= 255; i += plus1) {
    for (int led = r2; led <= LED_COUNT; led += 4) {
      strip.setPixelColor(led, i, 0, 0);
      strip.setPixelColor(led + 1, i, 0, 0);
    }
    strip.show();
    delay(delay1);   
  }

  delay(delay2);

  for (int j = 240; j >= idle; j -= plus2) {
    for (int led = r1; led <= LED_COUNT; led += 4) {
      strip.setPixelColor(led, j, 0, 0);
      strip.setPixelColor(led + 1, j, 0, 0);
    }
    strip.show();
    delay(delay1);   
  }

  for (int j = 240; j >= idle; j -= plus2) {
    for (int led = r2; led <= LED_COUNT; led += 4) {
      strip.setPixelColor(led, j, 0, 0);
      strip.setPixelColor(led + 1, j, 0, 0);
    }
    strip.show();
    delay(delay1);   
  }

  delay(delay3);
  
}



// EFFET TOUR EIFFEL
void effect_eiffel_tower() {

  int r1 = random(1, 21);
  r1 = (r1 * 4) - 4;

  strip.setPixelColor(r1, 255, 255, 255);
  strip.setPixelColor(r1 + 1, 255, 255, 255);
  strip.setPixelColor(r1 + 2, 255, 255, 255);
  strip.setPixelColor(r1 + 3, 255, 255, 255);

  int r2 = random(1, 21);

  if (r1 != r2) {
    
    r2 = (r2 * 4) - 4;
  
    strip.setPixelColor(r2, 255, 255, 255);
    strip.setPixelColor(r2 + 1, 255, 255, 255);
    strip.setPixelColor(r2 + 2, 255, 255, 255);
    strip.setPixelColor(r2 + 3, 255, 255, 255);
    
  }

  int r3 = random(1, 21);

  if (r3 != r1 && r3 != r2) {
    
    r3 = (r3 * 4) - 4;
  
    strip.setPixelColor(r3, 255, 255, 255);
    strip.setPixelColor(r3 + 1, 255, 255, 255);
    strip.setPixelColor(r3 + 2, 255, 255, 255);
    strip.setPixelColor(r3 + 3, 255, 255, 255);
    
  }
  
  strip.show();

  delay(50);

  strip.clear();
  
}






// UTILES POUR L'EFFET RAINBOW
void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i*1+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
    return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } 
  else if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } 
  else {
    WheelPos -= 170;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
