#include <SD.h>                // sd card

#include "RTClib.h"            // real time clock

#include "HT16K33.h"           // 4 digit displays vvv

#include <PololuLedStrip.h>    // led array

#include <Adafruit_CAP1188.h>  // touch sensors vvv

#include <Wire.h>

#include <SPI.h>

#include <Arduino.h>  // encoder vvv (new and shiny vers!)

#include <BasicEncoder.h>

#include <TimerOne.h>


int sdCardPin = 53;  // sd card, cs pin

File file;           // sd card [get file] name



RTC_Millis rtc;  // real time clock time set?


HT16K33 leftDigits(0x71);   // 4 digit i2c address & name define

HT16K33 rightDigits(0x70);  // 4 digit i2c address & name define

uint32_t counter = 0;       // 4 digit ?


#define CAP1188_SENSITIVITY 0x1F

#define CAP1188_RESET 9                                  // pin 9, run this line down all 3 sensors

Adafruit_CAP1188 cap = Adafruit_CAP1188(CAP1188_RESET);  // touch sensors vvv

Adafruit_CAP1188 touchSensor1(0x28);                     // i2c addresses + name defines vvv

Adafruit_CAP1188 touchSensor2(0x29);

Adafruit_CAP1188 touchSensor3(0x2B);


BasicEncoder encoder(3, 2);       // rotary pins

#define SW 4                      // roatary sw pin

unsigned long rotaryPressTime;    // counter/timer for how long it's held down

bool rotaryWaitForReset = false;  // if it's stopped being held down

int encoderPrev = 0;              // resets itsself to 0


const int writeLockPin = 7;  // write/lock switch

bool writeMode = false;      // bool for lock/unlock

bool touchButtons = false;   // diff from writeMode to prevent 'overwrite day w/ data already'


const int dayPlusPin = 5;   // day +-1 buttons

bool dayPlusState;          // as in prev state, for tracking press and un-press

const int dayMinusPin = 6;  // day +-1 buttons

bool dayMinusState;


int displayDayPrev = 0;

int displayDay = 0;  // first day (starting at 0)


void timer_service() {  // timer for rotary enc initiation

  encoder.service();

}


PololuLedStrip<12> ledStrip;                         // led input pin (only others 5v + gnd)

#define ledCount 24                                  // len(array)

rgb_color colors[ledCount];                          //

char colorLetter[26] = "bbbbbbbbbbbbbbbbbbbbbbbb"    // 24 of them

                       "\0";                         // keeps it from saving gibberish afterwards

char colorTouching[26] = "000000000000000000000000"  // current log state

                         "\0";

char colorTouchingPrev[26] = "000000000000000000000000"  // checking if state has changed

                             "\0";

bool touchJustOn = false;


void setup() {

  Serial.begin(9600);

  Serial.println("cool squares");

  Wire.begin();

  Wire.setClock(100000);  // 4 digits begin vvv

  leftDigits.begin();

  rightDigits.begin();

  leftDigits.displayOn();

  rightDigits.displayOn();

  pinMode(writeLockPin, INPUT);             // switch (button)

  pinMode(dayPlusPin, INPUT);               // button

  pinMode(dayMinusPin, INPUT);              // button

  pinMode(SW, INPUT_PULLUP);                // rotary sw

  Timer1.initialize(1000);                  // !!!!!how fast u can scroll on rotary!

  Timer1.attachInterrupt(timer_service);    // ^^^

  file = SD.open("FILE6.txt", FILE_WRITE);  // open "file.txt" to write data

  pinMode(sdCardPin, OUTPUT);               // sd card debug messages vvv

  if (!SD.begin(sdCardPin)) {

    Serial.println("Could not initialize SD card.");

  } else {

    Serial.println("yup there's an sd card");

  }

#ifndef ESP8266  // rtc vvv

  while (!Serial)

    ;  // wait for serial port to connect. Needed for native USB

#endif

  rtc.begin(DateTime(F(__DATE__), F(__TIME__)));  // January 21, 2014 at 3am == rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

  DateTime future(1600560000);                    // 9/20/20    (+ displayDay * 86400L)

  DateTime dt0(2020, 9, 20, 0, 0, 0);


  int maxDay = (rtc.now().unixtime() - 1600560000) / 86400L;  // days from 09/20/2020 --> [current day]

  Serial.print("Currently this many days: ");

  Serial.println(maxDay);


  if (!touchSensor1.begin(0x28)) {  // error messages- have to be here in some form for them to work?

    Serial.println("28 CAP1188 not found");

    touchSensor1.writeRegister(CAP1188_SENSITIVITY, 0x1F);  // 2x  sensitivity THIS SEEMS TO WORK THE BEST FOR 3.5" plate sensors

  } else {

    Serial.println("28 CAP1188 found!");

  }

  if (!touchSensor2.begin(0x29)) {

    Serial.println("29 CAP1188 not found");

    touchSensor2.writeRegister(CAP1188_SENSITIVITY, 0x1F);

  } else {

    Serial.println("29 CAP1188 found!");

  }

  if (!touchSensor3.begin(0x2B)) {

    Serial.println("2b CAP1188 not found");

    touchSensor2.writeRegister(CAP1188_SENSITIVITY, 0x1F);

  } else {

    Serial.println("2b CAP1188 found!");

  }

  if (!findDaysData()) {

    turnAllGrey();

  }

  digitsDisplayDay(0);  // digits to correct date

  updateLEDs();

}



void loop() {

  //finding out displayDate (scrollers always on) vvv

  bool dayPlusPrev = dayPlusState;                                      // day +1 button

  dayPlusState = digitalRead(dayPlusPin);                               // const updating reads of

  if (dayPlusState == LOW && dayPlusPrev == HIGH && displayDay >= 0) {  // +1 button has just been held & released

    displayDay += 1;

  }

  bool dayMinusPrev = dayMinusState;  // day -1 button

  dayMinusState = digitalRead(dayMinusPin);

  if (dayMinusState == LOW && dayMinusPrev == HIGH && displayDay > 0) {  // -1 button has just been held and released

    displayDay -= 1;

  }

  if (encoder.get_change()) {              // encoder & rotary same thing. if rotated...

    int encoderNow = encoder.get_count();  // gives const read of encoder position

    if (encoderNow > encoderPrev) {        // new date greater than prev & not gonna take us above max

      displayDay += 1;                     // only using increments of 1 bc sometimes it'd skip

    } else if (displayDay > 0) {           //new date less than prev & not gonna take us below zero

      displayDay -= 1;

    }

    encoderPrev = encoderNow;

  }



  // so now if any of those have been updated....   vvv

  if (displayDay != displayDayPrev) {

    printDate(displayDay);         // serial

    digitsDisplayDay(displayDay);  // updates 4 dig displays. not dependant on lock/unlock or data/noData

    if (!findDaysData()) {         // if NoData -- returns true or false if data

      turnAllGrey();

      if (writeMode) {  // if noData & we ARE writing, then buttons on (call on dayChange & writeLock change)

        touchButtons = true;

        touchJustOn = true;

      }

    } else {

      touchButtons = false;  // else theres data sorry :( press button to overwrite first (called in button press func)

    }

  }

  displayDayPrev = displayDay;


  if (digitalRead(writeLockPin) == HIGH) {  // HIGH is at top of breadboard and == unlocked

    if (!writeMode && !findDaysData()) {    // just switched from ! to on

      touchButtons = true;                  // call on writeLock change (& day change)

      touchJustOn = true;

    }

    writeMode = true;

    if (touchButtons == true) {  // butons had to get turned on from somewhere first...

      touchButtonsOn();          // gets to this everytime in loop

    }

  } else {                               // if LOW

    if (writeMode && !findDaysData()) {  // just switched from on to !

      touchButtons = false;

      turnAllGrey();

    } else if (writeMode && findDaysData()) {

    }

    writeMode = false;  // it just got locked

  }

  // vvv this just for rotary press (must b unlocked)!!!

  int currentRotaryState = digitalRead(SW);          // read of rotary encoder to see if it's being pressed, 1=up, 0=down

  if (!currentRotaryState && !rotaryWaitForReset) {  // if held down && not waiting to reset (Just been pressed, reset initialized false)

    rotaryPressTime = millis();                      // timer start

    rotaryWaitForReset = true;                       // reset start waiting

  }

  if (currentRotaryState && rotaryWaitForReset) {  // if not held down & it Is waiting for reset,

    rotaryWaitForReset = false;                    // it was just held and released. Exit func

    if (millis() - rotaryPressTime > 1500 && !writeMode) {

      printFile();  // optional for final- read out the file into serial

    }

    if (millis() - rotaryPressTime > 1500 && writeMode && findDaysData()) {                                 // LONG

      overWriteDay();                                                                                       // there was data. if noData, no need for this

    } else if (millis() - rotaryPressTime > 50 && writeMode && !findDaysData() && colorLetter[0] != 'g') {  // SHORT

      saveDay();                                                                                            // there's no data & i want to save

      touchButtons = false;

    }

  }


  delay(5);

}


void updateLEDs() {

  ledStrip.write(colors, ledCount);

}

void digitsDisplayDay(int displayDay) {               // displays regradless of lockBool & data/noData

  DateTime future(1600560000 + displayDay * 86400L);  // 09/20/20 + numb days since * len(day) in seconds

  leftDigits.displayColon(true);

  leftDigits.displayInt(future.month() * 100 + future.day());

  rightDigits.displayInt(future.year());

}


bool findDaysData() {  // func call to READ FILE + FIND CORRECT TEXT LINE (from displayDay != displayDayPrev)

  file = SD.open("FILE6.txt", FILE_READ);

  int lenFile = file.size();

  int lineNumb = 0;

  while (lineNumb < lenFile) {  // loop each line of file

    file.seek(lineNumb);        // find start index of line

    char character;             // resetting vars...

    int checkNumb = 0;

    int deciPlace = 1000;

    for (unsigned int i = 0; i < 4; i++) {  // loop the first 4 indexs of each line

      character = file.read();

      if (character != 48) {                        // if not zero (leaving in a fun bug for 2060 where checkNumb == 14456 (xxxx))

        checkNumb += (character - 48) * deciPlace;  // ascii translations

      }

      deciPlace = deciPlace / 10;  // just getting rid of extra '0's. char --> int

    }

    if (checkNumb == displayDay) {  // THIS DAY HAS DATA!

      showDayDataOnLEDs(lineNumb);  // new func call to init LEDs read from file

      file.close();

      return true;

    }

    lineNumb += 42;  // go to start of next line. (idk if lineNumb & i could be same var?)

  }

  Serial.println("no data for day :(");

  file.close();

  return false;

}


void showDayDataOnLEDs(int lineNumb) {  // func call to UPDATE LEDS READ FROM FILE

  file.seek(lineNumb + 16);             // move from start of line to where b&w array starts

  char character;

  for (unsigned int i = 0; i < 24; i++) {  // next 24 is the b&w array

    character = file.read();

    colorLetter[i] = character;

  }

  char tmp2[26] = "bbbbbbbbbbbbbbbbbbbbbbbb"

                  "\0";

  for (uint8_t i = 0; i < 24; i++) {  // strrev()??

    tmp2[i] = colorLetter[i];         // I PUT THE LED STRIP IN BACKWARDS P SURE NOO

  }                                   // buttons & led array line up but colorLetter is reverse of those

  int z2 = 23;                        // this just reverses colorLetter right b4 the save

  for (uint8_t i = 0; i < 24; i++) {  // :(

    colorLetter[i] = tmp2[z2];

    z2 -= 1;

  }

  for (unsigned int i = 0; i < 24; i++) {

    if (colorLetter[i] == 'w') {  // section bc colors is a weird pololulu thing :( vvv

      colors[i] = rgb_color(50, 50, 50);

    } else if (colorLetter[i] == 'b') {

      colors[i] = rgb_color(0, 0, 0);

    }

  }

  updateLEDs();

  Serial.println(colorLetter);

  return;

}


void turnAllGrey() {  // func call for whenever all LEDS DISPLAY GREY

  for (unsigned int i = 0; i < 24; i++) {

    colorLetter[i] = 'g';

    colors[i] = rgb_color(1, 1, 1);

  }

  updateLEDs();

}


void touchButtonsOn() {

  rgb_color w;

  w.red = 50;

  w.green = 50;

  w.blue = 50;

  rgb_color b;  // ideally want to declare these outside the loop- moving later xx

  b.red = 0;

  b.green = 0;

  b.blue = 0;

  rgb_color g;

  g.red = 1;

  g.green = 1;

  g.blue = 1;

  uint8_t touched1 = touchSensor1.touched();

  uint8_t touched2 = touchSensor2.touched();

  uint8_t touched3 = touchSensor3.touched();

  for (uint8_t i = 0; i < 8; i++) {  // for the 8 inputs...

    if (touched1 & (1 << i)) {       // if it's touched && shift i to i^2 ?

      colorTouching[i] = '1';        // then it should register as "touching" const while being touched.

    } else {

      colorTouching[i] = '0';  // const registers as not if not

    }

  }

  for (uint8_t i = 0; i < 8; i++) {  // 0-7, 8-15, 16-23

    if (touched2 & (1 << i)) {

      colorTouching[i + 8] = '1';

    } else {

      colorTouching[i + 8] = '0';

    }

  }

  for (uint8_t i = 0; i < 8; i++) {

    if (touched3 & (1 << i)) {

      colorTouching[i + 16] = '1';

    } else {

      colorTouching[i + 16] = '0';

    }

  }

  for (uint8_t i = 0; i < 24; i++) {                                                            // for the 24 inputs...

    if (colorTouching[i] != colorTouchingPrev[i] && colorTouching[i] == '1' && !touchJustOn) {  // its touchState has changed, & that state is To BeingTouched

      if (colorLetter[i] == 'w') {                                                              // if it was white now it's black

        colorLetter[i] = 'b';

        colors[i] = b;

      } else if (colorLetter[i] == 'b') {  // if it was black now its white

        colorLetter[i] = 'w';

        colors[i] = w;

      } else if (colorLetter[i] == 'g') {  // if it was grey: currently touching = b, all others = w

        for (uint8_t k = 0; k < 24; k++) {

          colorLetter[k] = 'w';

          colors[k] = w;

        }

        colorLetter[i] = 'b';

        colors[i] = b;

      }

    }

  }

  if (touchJustOn) {

    touchJustOn = false;

  }

  for (uint8_t i = 0; i < 24; i++) {

    colorTouchingPrev[i] = colorTouching[i];  // now log this state to b able 2 check next time if its changed

  }

  updateLEDs();

}


void overWriteDay() {                      // longPress && writeMode (&& dayData == true)

  file = SD.open("FILE6.txt", FILE_READ);  // stolen from findDaysData... perhaps could call this from there but eh idc

  int lenFile = file.size();

  int lineNumb = 0;

  while (lineNumb < lenFile) {  // loop each line of file

    file.seek(lineNumb);        // find start index of line

    char character;             // resetting vars...

    int checkNumb = 0;

    int deciPlace = 1000;

    for (unsigned int i = 0; i < 4; i++) {  // loop the first 4 indexs of each line

      character = file.read();

      if (character != 48) {                        // if not zero

        checkNumb += (character - 48) * deciPlace;  // ascii translations

      }

      deciPlace = deciPlace / 10;  // just getting rid of extra '0's. char --> int

    }

    if (checkNumb == displayDay) {  // FOUND THE CORRECT DAY

      file.close();

      file = SD.open("FILE6.txt", O_RDWR);  // not sure, but this is "overwrite" bc FILE_WRITE doesn't do it

      file.seek(lineNumb);

      for (unsigned int i = 0; i < 40; i++) {  // the "i"'s move the file's current read position apparentley

        file.print("x");                       // can't figure out how to delete! but this overwrites it fine

      }                                        // .txt file could be easily scrubbed for this later

      file.close();

      turnAllGrey();

      touchButtons = true;

      touchJustOn = true;

      return;

    }

    lineNumb += 42;

  }

}


void saveDay() {

  char tmp[26] = "bbbbbbbbbbbbbbbbbbbbbbbb"

                 "\0";

  for (uint8_t i = 0; i < 24; i++) {  // strrev()??

    tmp[i] = colorLetter[i];          // I PUT THE LED STRIP IN BACKWARDS P SURE NOO

  }                                   // buttons & led array line up but colorLetter is reverse of those

  int z = 23;                         // this just reverses colorLetter right b4 the save

  for (uint8_t i = 0; i < 24; i++) {  // :(

    colorLetter[i] = tmp[z];

    z -= 1;

  }


  DateTime future(1600560000 + displayDay * 86400L);  // 09/20/20 + numb days since * len(day) in seconds

  file = SD.open("FILE6.txt", FILE_WRITE);

  if (file) {

    if (displayDay < 10) {  // 0001 vvv, print actual numb

      file.print("000");

    } else if (displayDay < 100) {

      file.print("00");

    } else if (displayDay < 1000) {

      file.print("0");

    }

    file.print(displayDay);  // mo/dy/year with zeros vvv

    file.print(" ");

    if (future.month() < 10) {

      file.print('0');

    }

    file.print(future.month(), DEC);

    file.print('/');

    if (future.day() < 10) {

      file.print('0');

    }

    file.print(future.day(), DEC);

    file.print('/');

    file.print(future.year(), DEC);

    file.print(" ");

    file.println(colorLetter);  // 24 array

    file.close();

  } else {

    Serial.println("Could not open file (writing).");

  }

  Serial.print("Saved!");

  Serial.println(colorLetter);

}


void printDate(int displayDay) {  // func call for SERIAL PRINT

  if (displayDay < 10) {

    Serial.print("000");

  } else if (displayDay < 100) {

    Serial.print("00");

  } else if (displayDay < 1000) {

    Serial.print("0");

  }

  Serial.print(displayDay);  // this func is just for serial monitor. not saving to SD.

  Serial.print(" ");

  DateTime future(1600560000 + displayDay * 86400L);  // 09/20/20 + numb days since * len(day) in seconds

  if (future.month() < 10) {

    Serial.print('0');

  }

  Serial.print(future.month(), DEC);

  Serial.print('/');

  if (future.day() < 10) {

    Serial.print('0');

  }

  Serial.print(future.day(), DEC);

  Serial.print('/');

  Serial.print(future.year(), DEC);

  Serial.print(' ');

}




void printFile() {

  if (writeMode) {

    DateTime future(1600560000 + displayDay * 86400L);  // 09/20/20 + numb days since * len(day) in seconds

    file = SD.open("FILE6.txt", FILE_WRITE);

    if (file) {

      if (displayDay < 10) {  // 0001 vvv, print actual numb

        file.print("000");

      } else if (displayDay < 100) {

        file.print("00");

      } else if (displayDay < 1000) {

        file.print("0");

      }

      file.print(displayDay);  // mo/dy/year with zeros vvv

      file.print(" ");

      if (future.month() < 10) {

        file.print('0');

      }

      file.print(future.month(), DEC);

      file.print('/');

      if (future.day() < 10) {

        file.print('0');

      }

      file.print(future.day(), DEC);

      file.print('/');

      file.print(future.year(), DEC);

      file.print(" ");

      file.println(colorLetter);  // 24 array

      file.close();

    } else {

      Serial.println("Could not open file (writing).");

    }

  }

  file = SD.open("FILE6.txt", FILE_READ);  // this is entirely for Serial Print

  if (file) {

    Serial.println("--- Reading start ---");

    char character;

    while ((character = file.read()) != -1) {  // this while loop reads data stored in "file.txt" and prints it to serial monitor

      Serial.print(character);

    }

    file.close();

  } else {

    Serial.println("Could not open file (reading).");

  }

}