#include <Adafruit_NeoPixel.h>
#include "PWM.hpp"
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <Adafruit_ILI9341.h>
PWM mot_droit(2);
PWM mot_gauche(3);

#define PIN_NEON 4
#define PIN_FEUX_AVANT A3
#define PIN_FEUX_ARRIERE A4
#define NUMPIXELS 32

#define BLINK_INTERVAL 250
#define RADIUS_BLINK 100

#define OFFSET 30
#define WIDTH 130
#define HEIGHT 210
#define ROUND 50

#define TFT_CS 10
#define TFT_DC 9
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

#define DRAW_EYES tft.fillRoundRect(((tft.width() - HEIGHT)/2),((tft.height()/2) + OFFSET),HEIGHT,WIDTH,ROUND,white);tft.fillRoundRect(((tft.width() - HEIGHT)/2),((tft.height()/2) - OFFSET - WIDTH),HEIGHT,WIDTH,ROUND,white);
int looking = 0;

// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel feux_avant(NUMPIXELS, PIN_FEUX_AVANT, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel feux_arriere(NUMPIXELS, PIN_FEUX_ARRIERE, NEO_GRB + NEO_KHZ800);

boolean clignotant_droit = 1;
boolean clignotant_gauche = 1;

boolean clignotant_droit_state = 0;
boolean clignotant_gauche_state = 0;

long last_blink_time = 0;


enum colors{
	white = 0xFFFF,
  black = 0x0000,
  lgray = 0xBDF7,
  dgray = 0x7BEF,
  red = 0xF800,
  yellow = 0xFFE0,
  orange = 0xFBE0,
  brown = 0x79E0,
  green = 0x7E0,
  cyan = 0x7FF,
  blue = 0x1F,
  pink = 0xF81F
};

void setup_my_stuff() {
  Serial.begin(115200);
  tft.begin();
  pinMode(3,INPUT);
  pinMode(2,INPUT);

  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(A1,INPUT);
  pinMode(A2,INPUT);

  mot_gauche.begin(true);
  mot_droit.begin(true);

  feux_avant.begin();
  feux_arriere.begin();

  feux_avant.clear();
  feux_avant.setPixelColor(1, feux_avant.Color(255, 255, 255));
  feux_avant.setPixelColor(2, feux_avant.Color(255, 255, 255));
  feux_avant.show();

  feux_arriere.clear();
  feux_arriere.setPixelColor(1, feux_avant.Color(255, 0, 0));
  feux_arriere.setPixelColor(2, feux_avant.Color(255, 0, 0));
  feux_arriere.show();

  tft.fillRoundRect(((tft.width() - HEIGHT)/2),((tft.height()/2) + OFFSET),HEIGHT,WIDTH,ROUND,white);
  tft.fillRoundRect(((tft.width() - HEIGHT)/2),((tft.height()/2) - OFFSET - WIDTH),HEIGHT,WIDTH,ROUND,white);

}

void do_my_things() {
  if(millis() - last_blink_time >= BLINK_INTERVAL){
    last_blink_time = millis();

    if(clignotant_droit){
      clignotant_droit_state = !clignotant_droit_state;
      if (clignotant_droit_state){
        feux_avant.setPixelColor(0, feux_avant.Color(255, 128, 0));
        feux_arriere.setPixelColor(0, feux_arriere.Color(255, 128, 0));
      } else {
        feux_avant.setPixelColor(0, feux_avant.Color(0, 0, 0));
        feux_arriere.setPixelColor(0, feux_arriere.Color(0, 0, 0));
      }
      feux_avant.show();
      feux_arriere.show();
    } else {
        feux_avant.setPixelColor(3, feux_avant.Color(0, 0, 0));
        feux_arriere.setPixelColor(3, feux_arriere.Color(0, 0, 0));
        feux_avant.show();
        feux_arriere.show();
    }

    if(clignotant_gauche){
      clignotant_gauche_state = !clignotant_gauche_state;
      if (clignotant_gauche_state){
        feux_avant.setPixelColor(3, feux_avant.Color(255, 128, 0));
        feux_arriere.setPixelColor(3, feux_arriere.Color(255, 128, 0));
      } else {
        feux_avant.setPixelColor(3, feux_avant.Color(0, 0, 0));
        feux_arriere.setPixelColor(3, feux_arriere.Color(0, 0, 0));
      } 
      feux_avant.show();
      feux_arriere.show();
    } else {
        feux_avant.setPixelColor(3, feux_avant.Color(0, 0, 0));
        feux_arriere.setPixelColor(3, feux_arriere.Color(0, 0, 0));
        feux_avant.show();
        feux_arriere.show();
    }
  }

  analogWrite(5,analogRead(A1)/4);
  analogWrite(6,analogRead(A2)/4);

  int turn_radius = mot_droit.getValue() - mot_gauche.getValue(); 
  clignotant_gauche = turn_radius < -RADIUS_BLINK;
  clignotant_droit = turn_radius > RADIUS_BLINK;
  //Serial.print(mot_gauche.getValue());Serial.print("\t");Serial.print(mot_droit.getValue());Serial.print("\t");Serial.println(turn_radius);


  if(clignotant_droit && looking != 3){
    DRAW_EYES
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 50),((tft.height()/2) + OFFSET + WIDTH/4 - 25),WIDTH/2+20,WIDTH/2,ROUND,black);
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 50),((tft.height()/2) - OFFSET - WIDTH + WIDTH/4 - 25),WIDTH/2+20,WIDTH/2,ROUND,black);
    looking = 3;
    mot_droit.getValue();mot_gauche.getValue(); 
  } else if (clignotant_gauche && looking != 1){
    DRAW_EYES
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 50),((tft.height()/2) + OFFSET + WIDTH/4 + 25),WIDTH/2+20,WIDTH/2,ROUND,black);
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 50),((tft.height()/2) - OFFSET - WIDTH + WIDTH/4 + 25),WIDTH/2+20,WIDTH/2,ROUND,black);
    looking = 1;
    mot_droit.getValue();mot_gauche.getValue(); 
  } else if (!clignotant_droit && !clignotant_gauche && looking != 2){
    DRAW_EYES
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 20),((tft.height()/2) + OFFSET + WIDTH/4),WIDTH/2+20,WIDTH/2,ROUND,black);
    tft.fillRoundRect(((tft.width() - HEIGHT)/2 + 20),((tft.height()/2) - OFFSET - WIDTH + WIDTH/4),WIDTH/2+20,WIDTH/2,ROUND,black);
    looking = 2;
    mot_droit.getValue();mot_gauche.getValue(); 
  }
  
}





class Strip
{
public:
  uint8_t   effect;
  uint8_t   effects;
  uint16_t  effStep;
  unsigned long effStart;
  Adafruit_NeoPixel strip;
  Strip(uint16_t leds, uint8_t pin, uint8_t toteffects, uint16_t striptype) : strip(leds, pin, striptype) {
    effect = -1;
    effects = toteffects;
    Reset();
  }
  void Reset(){
    effStep = 0;
    effect = (effect + 1) % effects;
    effStart = millis();
  }
};

struct Loop{
  uint8_t currentChild;
  uint8_t childs;
  bool timeBased;
  uint16_t cycles;
  uint16_t currentTime;
  Loop(uint8_t totchilds, bool timebased, uint16_t tottime) {currentTime=0;currentChild=0;childs=totchilds;timeBased=timebased;cycles=tottime;}
};

Strip strip_0(NUMPIXELS, PIN_NEON, NUMPIXELS, NEO_GRB + NEO_KHZ800);
struct Loop strip0loop0(1, false, 1);

//[GLOBAL_VARIABLES]

void setup() {
  setup_my_stuff();
  strip_0.strip.begin();
}

void loop() {
  do_my_things();
  strips_loop();
}

void strips_loop() {
  if(strip0_loop0() & 0x01)
    strip_0.strip.show();
}

uint8_t strip0_loop0() {
  uint8_t ret = 0x00;
  switch(strip0loop0.currentChild) {
    case 0: 
           ret = strip0_loop0_eff0();break;
  }
  if(ret & 0x02) {
    ret &= 0xfd;
    if(strip0loop0.currentChild + 1 >= strip0loop0.childs) {
      strip0loop0.currentChild = 0;
      if(++strip0loop0.currentTime >= strip0loop0.cycles) {strip0loop0.currentTime = 0; ret |= 0x02;}
    }
    else {
      strip0loop0.currentChild++;
    }
  };
  return ret;
}

uint8_t strip0_loop0_eff0() {
    // Strip ID: 0 - Effect: Rainbow - LEDS: 32
    // Steps: 60 - Delay: 20
    // Colors: 3 (255.0.0, 0.255.0, 0.0.255)
    // Options: rainbowlen=60, toLeft=true, 
  if(millis() - strip_0.effStart < 20 * (strip_0.effStep)) return 0x00;
  float factor1, factor2;
  uint16_t ind;
  for(uint16_t j=0;j<32;j++) {
    ind = strip_0.effStep + j * 1;
    switch((int)((ind % 60) / 20)) {
      case 0: factor1 = 1.0 - ((float)(ind % 60 - 0 * 20) / 20);
              factor2 = (float)((int)(ind - 0) % 60) / 20;
              strip_0.strip.setPixelColor(j, 255 * factor1 + 0 * factor2, 0 * factor1 + 255 * factor2, 0 * factor1 + 0 * factor2);
              break;
      case 1: factor1 = 1.0 - ((float)(ind % 60 - 1 * 20) / 20);
              factor2 = (float)((int)(ind - 20) % 60) / 20;
              strip_0.strip.setPixelColor(j, 0 * factor1 + 0 * factor2, 255 * factor1 + 0 * factor2, 0 * factor1 + 255 * factor2);
              break;
      case 2: factor1 = 1.0 - ((float)(ind % 60 - 2 * 20) / 20);
              factor2 = (float)((int)(ind - 40) % 60) / 20;
              strip_0.strip.setPixelColor(j, 0 * factor1 + 255 * factor2, 0 * factor1 + 0 * factor2, 255 * factor1 + 0 * factor2);
              break;
    }
  }
  if(strip_0.effStep >= 60) {strip_0.Reset(); return 0x03; }
  else strip_0.effStep++;
  return 0x01;
}