//Kevin Rye - 2016
//compiled on Arduino 1.6.7
//This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
//http://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_US

#include <Wire.h>
#include <SPI.h> //for VS1053
#include <Adafruit_VS1053.h> //for VS1053
#include <SD.h> //for VS1053
#include <Sodaq_DS3231.h> //for DateTime dt
#include <Adafruit_NeoPixel.h>

// define the pins used
//#define CLK 13       // SPI Clock, shared with SD card
//#define MISO 12      // Input data, from VS1053/SD card
//#define MOSI 11      // Output data, to VS1053/SD card
// Connect CLK, MISO and MOSI to hardware SPI pins.
// See http://arduino.cc/en/Reference/SPI "Connections"

// These are the pins used for the breakout example
#define BREAKOUT_RESET  9      // VS1053 reset pin (output)
#define BREAKOUT_CS     10     // VS1053 chip select pin (output)
#define BREAKOUT_DCS    8      // VS1053 Data/command select pin (output)
// These are the pins used for the music maker shield
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)

// These are common pins between breakout and shield
#define CARDCS 4     // Card chip select pin
// DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin

Adafruit_VS1053_FilePlayer musicPlayer =
  // create breakout-example object!
  Adafruit_VS1053_FilePlayer(BREAKOUT_RESET, BREAKOUT_CS, BREAKOUT_DCS, DREQ, CARDCS);
// create shield-example object!
//Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);

//date calc stuff
#define CalendarYrToTm(Y) ((Y) - 1970)
#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400)))
static  const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0

//Neopixels
#define PIXEL_PIN        6

// How many NeoPixels are attached to the Arduino?
#define PIXEL_COUNT      24
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

//I/O
const int playButton = 2;
const int upHrsButton = A0;
const int downHrsButton = A1 ;
const int upMinsButton = A2;
const int downMinsButton = A3;

//audio
int enableAmplifier = 7;

//RTC stuff
int seconds = 0;
int minutes = 0;
int hours = 12;
int adjHours;
int dayOfWeek; //not used (but needed for DS3231 function), DOW is calculated and pushed to 'returnedDOWvar'
int dayOfMonth = 1;
int month = 1;
int year = 16;
//year, month, date, hour, min, sec and week-day //starts from 1 (sun) and goes to 7 (sat)
//DateTime dt(2016, 5, 29, 17, 53, 0, 1);

char timeString1[7]; //H:M
char timeString2[8]; //HH:M
char timeString3[7]; //H:MM
char timeString4[8]; //HH:MM

void setup() {

  pinMode(upHrsButton, INPUT);
  pinMode(downHrsButton, INPUT);
  pinMode(upMinsButton, INPUT);
  pinMode(downMinsButton, INPUT);
  pinMode(enableAmplifier, OUTPUT);
  digitalWrite(enableAmplifier, LOW);

  Serial.begin(57600);
  Wire.begin();
  //rtc.setDateTime(dt); //Adjust date-time as defined 'dt' above

  strip.begin();
  colorWipe(strip.Color(0, 0, 255), 50);  // Blue
  strip.show();

  if (! musicPlayer.begin()) { // initialise the music player
    //Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
    while (1);
  }
  //Serial.println(F("VS1053 found"));

  SD.begin(CARDCS);    // initialise the SD card

  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(1, 1);

  // Timer interrupts are not suggested, better to use DREQ interrupt!
  //musicPlayer.useInterrupt(VS1053_FILEPLAYER_TIMER0_INT); // timer int

  // If DREQ is on an interrupt pin (on uno, #2 or #3) we can do background
  // audio playing
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int

} //end setup

void loop() {

  //poll the DS3231 for the date and time
  get_time();

  //pass RTC values into a new variable -
  //this way one variable stores time in 24 hour mode and another that can be converted to 12 hour mode
  //time is monitored 'in the background' in 24 hour mode using the RTC, but is displayed in an adjusted 12-hr format
  adjHours = hours;

  //convert to 12 hr mode for display
  if (hours < 1) {
    adjHours = 12;
  }

  if ((hours > 12) && (hours < 24)) {
    adjHours = hours - 12;
  }

  //handle button presses
  if (digitalRead(playButton) == LOW) {

    //play audio
    announceTheTime();
    Serial.println("is talking");
  }

  //announce the time on the hour
  //only announce the time from 7 AM up to midnight
  if ((hours > 6) && (hours < 24) && (minutes == 0 ) && (seconds == 0)) {

    announceTheTime();

  }

  //animate neopixels while audio is playing
  if (musicPlayer.playingMusic) {
    digitalWrite(enableAmplifier, HIGH);
    theaterChase(strip.Color(0, 0, 255), 100); // on
    strip.show();

    Serial.println("is talking, auto");
  }
  else {
    digitalWrite(enableAmplifier, LOW);
    theaterChase(strip.Color(0, 0, 255), 0); // off
    strip.show();

  }

  //set time/date
  if (digitalRead(upHrsButton) == LOW) { //UP button pressed, increment value of selected field

    //debaounce
    delay(200);
    hours++;

    if (hours > 23) {
      hours = 0;
    }

    set_time();
  }

  if (digitalRead(upMinsButton) == LOW) { //UP button pressed, increment value of selected field

    //debaounce
    delay(200);

    minutes++;
    seconds = 0;

    if (minutes > 59) {
      minutes = 0;
    }

    set_time();
  }

  if (digitalRead(downHrsButton) == LOW) { //UP button pressed, increment value of selected field

    //debaounce
    delay(200);

    hours--; // subtract day
    if (hours < 0) {

      hours = 23; // loop back
    }

    set_time();
  }

  if (digitalRead(downMinsButton) == LOW) { //UP button pressed, increment value of selected field

    //debaounce
    delay(200);

    minutes--; // subtract day
    if (minutes < 0) minutes = 59; // loop back
    seconds = 0;

    set_time();
  }

} //end loop

void announceTheTime() {

  delay(200); //stops ('seconds == 0' up top from being true on the second loop around

  //mp3 file name constructor
  if ((adjHours < 10) && (minutes < 10)) {
    sprintf(timeString1, "%d0%d.mp3", adjHours, minutes);
  }
  else if ((adjHours > 9) && (minutes < 10)) {
    sprintf(timeString2, "%d0%d.mp3", adjHours, minutes);
  }
  else if ((adjHours < 10) && (minutes > 9)) {
    sprintf(timeString3, "%d%d.mp3", adjHours, minutes);
  }
  else if ((adjHours > 9) && (minutes > 9)) {
    sprintf(timeString4, "%d%d.mp3", adjHours, minutes);
  }

  //play string
  if ((adjHours < 10) && (minutes < 10)) {
    musicPlayer.startPlayingFile(timeString1);
  }
  else if ((adjHours > 9) && (minutes < 10)) {
    musicPlayer.startPlayingFile(timeString2);
  }
  else if ((adjHours < 10) && (minutes > 9)) {
    musicPlayer.startPlayingFile(timeString3);
  }
  else if ((adjHours > 9) && (minutes > 9)) {
    musicPlayer.startPlayingFile(timeString4);
  }
}

//DS3231 stuff
void set_time() {
  Wire.beginTransmission(104);
  Wire.write(0);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.endTransmission();
}

void get_time() {
  Wire.beginTransmission(104);
  Wire.write(0);//set register to 0
  Wire.endTransmission();
  Wire.requestFrom(104, 3);//get 3 bytes (seconds,minutes,hours);
  seconds = bcdToDec(Wire.read() & 0x7f);
  minutes = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
}

byte decToBcd(byte val) {
  return ((val / 10 * 16) + (val % 10));
}

byte bcdToDec(byte val) {
  return ((val / 16 * 10) + (val % 16));
}

//Neopixel
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 2; j++) { //do 2 cycles of chasing
    for (int q = 0; q < 2; q++) {
      for (int i = 0; i < strip.numPixels(); i = i + 2) {
        strip.setPixelColor(i + q, c);  //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i = 0; i < strip.numPixels(); i = i + 2) {
        strip.setPixelColor(i + q, 0);      //turn every third pixel off
      }
    }
  }
}

void colorWipe(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

