/**********************************************************
  This sketch shows the use of SPI Driver used in Snooze
  along with the standard SD and Bounce libraries. In this
  example the Teensy SD Data-Logger will deepSleep until you
  push the button (WAKEUP_BUTTON) which will wake the Teensy
  and start logging data until the button is released in
  which the Teensy will go back into deepSleep.

  The SPI Driver configures the SDIO or SPI pins to minimize
  leakage current during deepSleep and reconfigure the pins
  back after wake up.
 ***********************************************************/

/***********************************************************
  SNOOZE
 ***********************************************************/
#include <Snooze.h>
// Load drivers
// Always use SnoozeUSBSerial with T4.0.
// SnoozeUSBSerial driver does what Serial does like "print", -
// "println", "prinf", etc and also handles the effects of using -
// the Arduino Serial monitor and sleeping. Use it when you -
// want to print to serial monitor for sleeping applications.
SnoozeUSBSerial   usb;
SnoozeDigital     digital;
SnoozeSPI         sdCard;

SnoozeBlock config(usb, digital, sdCard);
/***********************************************************
  SD
 ***********************************************************/
#include <SD.h>
#include <SPI.h>

// set up variables using the SD utility library functions:
Sd2Card  card;
SdVolume volume;
SdFile   root;

const int chipSelect = BUILTIN_SDCARD;
/***********************************************************
  BOUNCE
 ***********************************************************/
#include <Bounce.h>
const uint8_t WAKEUP_BUTTON = 7;
// use bounce for pin 7, debounce of 5ms
Bounce button = Bounce(WAKEUP_BUTTON, 5);
/***********************************************************
  TIME
 ***********************************************************/
#include <TimeLib.h>
elapsedMillis rtcMilliSeconds;
/***********************************************************/

uint32_t dataCount;
// ----------------------------------------------------------------------------------------------
void setup() {
  //
  setSyncProvider(getTeensy3Time);
  //
  dataCount = 0;
  // Configure LED Pin
  pinMode(LED_BUILTIN, OUTPUT);
  // Configure pin 21 for bounce library
  pinMode(WAKEUP_BUTTON, INPUT_PULLUP);

  /******************Snooze Driver Config******************/
  // Configure pin WAKEUP_BUTTON for Snooze wakeup, also -
  // used by bounce but no need to reconfigure the pin -
  // after waking Snooze does it for you.
  digital.pinMode(WAKEUP_BUTTON, INPUT_PULLUP, FALLING);//pin, mode, type
  // Configure SD Card for Low Power operation
  sdCard.setClockPin( BUILTIN_SDCARD );
  /****************End Snooze Driver Config****************/

  // UNCOMMENT THESE TWO LINES FOR TEENSY AUDIO BOARD:
  //SPI.setMOSI(7);  // Audio shield has MOSI on pin 7
  //SPI.setSCK(14);  // Audio shield has SCK on pin 14

  // Wait for Serial connection to open.
  while (!usb);
  delay(100);

  usb.println("Snooze SD Card Push Button Hold Data-Logger Example");
  usb.println("---------------------------------------------------");
  // see if the card is present and can be initialized:
  SD.begin(chipSelect);
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    // don't do anything more:
    while (1) {
      usb.println("Card failed, or not present!");
      blink();
      blink();
    }
  }
  elapsedMillis time = 0;
  while (time < 1000) {
    usb.print(".");
    delay(200);
  }
  usb.println();
  usb.print("-> ");
  usb.println("SD Card Initialized.");
  sdCardInfo();
  delay(1000);
}
// ------------------------------------------------------------------------
void loop() {
  sleepDataLogger();
  handleButtonHold();
}
// ------------------------------------------------------------------------
inline void sleepDataLogger() {
SLEEP:
  digitalWriteFast(LED_BUILTIN, LOW);
  usb.println("Logger Will Sleep Now...");
  // delay to finish usb print before sleeping
  delay(5);
  // you need to update bounce before sleeping.
  button.update();
  // put Teensy into low power sleep mode
  Snooze.deepSleep( config );
  digitalWriteFast(LED_BUILTIN, HIGH);

  // check for 300ms that the button is held
  bool hold = threeHundredMilliSecondHoldCheck();

  // if not held for 300ms go back to sleep
  if (hold == false) goto SLEEP;

  usb.println("Data Logger now awake, ready to log data.");
}
// ------------------------------------------------------------------
// Make sure the button is help low for aleast 300ms before data logging.
bool threeHundredMilliSecondHoldCheck() {
  button.update();
  // Pin is configured as PULLUP so 0 means the button has been
  // pushed.
  while (button.read() == 0) {
    // Monitor the pin hold duration.
    if (button.duration() > 300) return true;
    // Update bounce library.
    button.update();
  }
  return false;
}
// ------------------------------------------------------------------------
void handleButtonHold() {
  button.update();
  if (button.read() == HIGH) {
    usb.println("No Logging of Data!!! Button released before logging could commence.");
    return;
  }
  usb.println("Logging data");
  button.update();
  elapsedMillis log_time = 0;
  while (button.read() == LOW) {
    if (log_time >= 99) {
      log_time = 0;
      logData(dataCount++);
    }
    button.update();
  }
  digitalWrite(LED_BUILTIN, LOW);
}
// ------------------------------------------------------------------------
void logData(uint32_t count) {
  // make a string for assembling the data to log:
  String dataString;//"Time\tAna1\tAna2\tAna3\r\n";
  // read three sensors and append to the string:
  dataString = digitalClockDisplay(rtcMilliSeconds - 1);
  dataString += "\t";
  dataString += String(count);
  dataString += String("\t");
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    dataString += analogRead(analogPin);
    if ( analogPin < 2) dataString += "\t";
  }
  dataString += "\r\n";
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(dataString);
    dataFile.close();
    // print to the serial port too:
    int len = dataString.length();
    usb.print(dataString);
  }
  // if the file isn't open, pop up an error:
  else {
    usb.println("error opening datalog.txt.");
  }
}
// ------------------------------------------------------------------------
void sdCardInfo() {
  // print the type of card
  usb.print("-> Card type: ");

  switch (card.type()) {
    case SD_CARD_TYPE_SD1: {
        usb.println("SD1");
        break;
      }
    case SD_CARD_TYPE_SD2: {
        usb.println("SD2");
        break;
      }
    case SD_CARD_TYPE_SDHC: {
        usb.println("SDHC");
        break;
      }
    default: {
        usb.println("Unknown");
        break;
      }
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    usb.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }

  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  usb.print("\nVolume type is FAT");
  usb.println(volume.fatType(), DEC);
  usb.println();
  // clusters are collections of blocks
  volumesize = volume.blocksPerCluster();
  // we'll have a lot of clusters
  volumesize *= volume.clusterCount();
  if (volumesize < 8388608ul) {
    usb.print("Volume size (bytes): ");
    // SD card blocks are always 512 bytes
    usb.println(volumesize * 512);
  }
  usb.print("Volume size (Kbytes): ");
  volumesize /= 2;
  usb.println(volumesize);
  usb.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  usb.println(volumesize);

  usb.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
  delay(100);
}
// ------------------------------------------------------------------------
void rtc_seconds_isr() {
  // zero out millisecond counter
  rtcMilliSeconds = 0;
}

time_t getTeensy3Time() {
  // set rtc seconds interrupt
  //RTC_IER |= 0x10;
  rtcMilliSeconds = 0;
  // enable interrupt
  //NVIC_ENABLE_IRQ(IRQ_RTC_SECOND);
  return Teensy3Clock.get();
}
// ------------------------------------------------------------------------
String digitalClockDisplay(elapsedMillis millis_t) {

  String time;

  if (hour() < 10) time += 0;
  //< 10 ? '0' + hour() : hour();
  time += hour();
  time += ":";

  if (minute() < 10) time += 0;
  //< 10 ? '0' + minute() : minute();
  time += minute();
  time += ":";

  if (second() < 10) time += 0;
  time += second();
  time += ".";

  if (millis_t < 100) time += 0;
  if (millis_t < 10) time += 0;
  time += (int)millis_t;

  return time;
}
// ------------------------------------------------------------------------
void blink() {
  digitalWriteFast(LED_BUILTIN, HIGH);
  delay(15);
  digitalWriteFast(LED_BUILTIN, LOW);
  delay(15);
}
