#define ESP8266
#include <Adafruit_NeoPixel.h>
#include <Ticker.h>

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>

/************************* Neopixel Setup ************************************/
#define LED_PIN          D4
#define NUMPIXELS        40

volatile bool leds_on = false;

//This will change depending on the specific lights that you have
Adafruit_NeoPixel led_tree = Adafruit_NeoPixel(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ400);


/************************* WiFi Setup **************************************/
#define WIFI_SSID       "WIFI-SSID"
#define WIFI_PASS       "WIFI-PASS"

WiFiClient client;


/*********************** AdafruitIO Setup **********************************/

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "AIO-USERNAME"
#define AIO_KEY         "AIO-KEY"

Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Subscribe onoff = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");


/************************* LED Controls *************************************/
//Green, for the body of the tree
int R_1 = 0x00;
int G_1 = 0xF0;
int B_1 = 0x00;

//Brighter red, for the initial light color.
int R_2 = 0xFF;
int G_2 = 0x00;
int B_2 = 0x00;

int last_rand = 0xFF;

Ticker light_ticker;


/************************* Sketch Code *************************************/
void setup() 
{
  Serial.begin(115200);
  delay(10);

  //Give the random number generator a fairly random seed.
  randomSeed(analogRead(0));

  //Start the neopixel library
  led_tree.begin();
  Serial.println("Started!");

  //Connect to the defined WiFi network
  Serial.print("Connecting to Wifi: ");
  Serial.print(WIFI_SSID);

  uint8_t counter = 0;
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED && counter < 10) 
  {
    delay(500);
    Serial.print(".");
    counter++;
  }

  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("\nFailed to connect to WiFi...");
  }
  else
  {
    Serial.println("\nConnected!");

    //Subscribe to the onoff feed
    mqtt.subscribe(&onoff);

    //Turn the tree on
    refresh_tree();
  }
}

void loop() 
{
  //Now, each time through the loop, we check out Adafruit IO feed for an update

  //Connect or Reconnect to AdafruitIO
  MQTT_connect(); 

  //Wait up to 5 seconds for the feed to send an update, then process it.
  Adafruit_MQTT_Subscribe *feed_update;
  while ((feed_update = mqtt.readSubscription(5000)))
  {
    //Check which feed updated - not neccesary, since there's only one, but good practice
    if (feed_update == &onoff)
    {
      //Print the update to the serial monitor
      Serial.print("onoff: ");
      Serial.println((char*) onoff.lastread);

      if (!strcmp((char*) onoff.lastread, "ON"))
      {
        leds_on = true;
        
        //Start the interrupt driven function that chooses a light color
        light_ticker.attach(3.0, update_color);
      }
      else
      {
        leds_on = false;

        //Stop the interrupt driven function that chooses a light color
        light_ticker.detach();
      }

      refresh_tree();
    }
  }
  
}


/*********************** Helper Functions **********************************/
//Choose a new color for the accent lights.
void update_color()
{
  //Randomly choose a color for the lights (but don't do the same twice...)
  int new_rand = last_rand;

  while (new_rand == last_rand)
  {
    new_rand = random(4);
  }
  
  switch (new_rand)
  {
    case 0:
      R_2 = 0xFF;
      G_2 = 0x00;
      B_2 = 0x00;
      Serial.println("Now RED");
    break;
    
    case 1:
      R_2 = 0x00;
      G_2 = 0x00;
      B_2 = 0xFF;
      Serial.println("Now BLUE");
    break;
    
    case 2:
      R_2 = 0xFF;
      G_2 = 0x00;
      B_2 = 0xFF;
      Serial.println("Now MAGENTA");
    break;
    
    case 3:
      R_2 = 0x00;
      G_2 = 0xFF;
      B_2 = 0xFF;
      Serial.println("Now CYAN");
    break;
  }

  refresh_tree();

  last_rand = new_rand;
}

void refresh_tree()
{
  uint8_t r;
  uint8_t g;
  uint8_t b;

  for (int i = 0; i < NUMPIXELS; i++)
  {
    if (leds_on == false)
    {
      r = 0;
      g = 0;
      b = 0;
    }
    else if (i % 4 == 0)
    {
      r = R_2;
      g = G_2;
      b = B_2;
    }
    else
    {
      r = R_1;
      g = G_1;
      b = B_1;
    }
       
    led_tree.setPixelColor(i, r, g, b);
  }

  Serial.println("Tree updated!");
  led_tree.show();
}


/**************************************************************************
  Adafruit MQTT Library ESP8266 Example

  Must use ESP8266 Arduino from:
    https://github.com/esp8266/Arduino

  Works great with Adafruit's Huzzah ESP board & Feather
  ----> https://www.adafruit.com/product/2471
  ----> https://www.adafruit.com/products/2821

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Tony DiCola for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 *************************************************************************/
void MQTT_connect() 
{
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) 
  {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) // connect will return 0 for connected
  { 
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       retries--;
       if (retries == 0) 
       {
         // basically die and wait for WDT to reset me
         while (1);
       }
  }
  Serial.println("MQTT Connected!");
}
