#include <LiquidCrystal.h>
#include<Wire.h>
#include "RTClib.h"
#include <string.h>
#define CLK 7
#define DT 8
#define SW 9
#define seconds() (millis()/1000)
#include <MemoryFree.h>
//Exccessive variable time !!!
String whole = "";
int Iteration = 0;
int BootM = 0;
int beginM = millis();
bool cursored = false;
int UpdatedDate = 0;
String status = "none";
String inputString = "";
int DailyCode[36];
int selection = 0;
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
unsigned long lastButtonPress = 0;
String screen = "HM";
bool refreshDisplay = true;
/*
   HM - Home (CLOCK)
   MM - Main Menu
   TM - Tempurature Menu
   FM - Forcast Menu
   TT - Temp Today
   TW - Temp Tomorrow
   DW - Daily Weather
   HW - Hourly Weather
*/

const byte Sunny[] PROGMEM = {
  B00100,
  B10101,
  B01110,
  B11111,
  B01110,
  B10101,
  B00100,
  B00000
};
const byte partlyCloudy[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B10001,
  B10101,
  B01010,
  B00000,
  B00000
};

const byte veryCloudy[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B11111,
  B11111,
  B01010,
  B00000,
  B00000
};
const byte SlightDrizzle[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B10001,
  B11111,
  B00000,
  B10101,
  B00000
};
const byte MuchDrizzle[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B11111,
  B11111,
  B00000,
  B10101,
  B00000
};
const byte LightRain[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B10001,
  B11111,
  B00000,
  B10101,
  B10101
};
const byte HeavyRain[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B11111,
  B11111,
  B00000,
  B10101,
  B10101
};
const byte FloodStorm[] PROGMEM = {
  B00000,
  B00000,
  B01110,
  B11111,
  B11111,
  B00000,
  B01010,
  B11111
};
const byte Snow[] PROGMEM = {
  B01010,
  B00000,
  B10101,
  B00000,
  B01010,
  B00000,
  B10101,
  B00000
};
const byte Connecting[] PROGMEM = {
  B00000,
  B11111,
  B10001,
  B00000,
  B01110,
  B01010,
  B00000,
  B00100
};
const byte Connected[] PROGMEM = {
  B00000,
  B01110,
  B01010,
  B01000,
  B01110,
  B01110,
  B01110,
  B00000
};
const byte Failure[] PROGMEM = {
  B11111,
  B10001,
  B10101,
  B10101,
  B10001,
  B10001,
  B10001,
  B11111
};
const byte Downloading[] PROGMEM = {
  B00000,
  B01110,
  B01110,
  B01110,
  B11111,
  B01110,
  B00100,
  B00000
};
const byte Receiving[] PROGMEM = {
  B11111,
  B10001,
  B10001,
  B10001,
  B00000,
  B10001,
  B11011,
  B11111
};
const byte Done[] PROGMEM = {
  B00000,
  B00001,
  B00001,
  B00010,
  B01010,
  B00100,
  B00000,
  B00000
};








bool clocker = false;
bool com = false;
RTC_DS1307 RTC;
int weekday(int year, int month, int day)
/* Calculate day of week in proleptic Gregorian calendar. Sunday == 0. */
{
  int adjustment, mm, yy;
  if (year < 2000) year += 2000;
  adjustment = (14 - month) / 12;
  mm = month + 12 * adjustment - 2;
  yy = year - adjustment;
  return (day + (13 * mm - 1) / 5 +
          yy + yy / 4 - yy / 100 + yy / 400) % 7;
}

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
DateTime now;
void createCharByte(byte pos, const byte arr[]) {
  byte temp[8];
  for (int i = 0; i < 8; i++) {
    temp[i] = pgm_read_byte(&arr[i]);
  }
  lcd.createChar(pos, temp);
}
void setup() {
  lcd.begin(16, 2);

  Serial.begin(9600);
  lcd.write("Ready...");
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(6, OUTPUT);
  RTC.begin();
  createCharByte(0, Connecting);
  createCharByte(1, Connected);
  createCharByte(2, Downloading);
  createCharByte(3, Receiving);
  createCharByte(4, Done);
  createCharByte(5, Failure);
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);
  lastStateCLK = digitalRead(CLK);
  lcd.home();
  now = RTC.now();
  Clocked();
  digitalWrite(6, HIGH);
  UpdatedDate = now.day();
}
void WCode(int WC) {
  switch (WC) {
    case 0: case 1:
      createCharByte(1, Sunny);
      break;
    case 2: case 45: case 48:
      //partlyCloudy
      createCharByte(1, partlyCloudy);
      break;
    case 3:
      //veryCloudy
      createCharByte(1, veryCloudy);
      break;
    case 51: case 53: case 56:
      //SlightDrizzle
      createCharByte(1, SlightDrizzle);
      break;
    case 55: case 57:
      //MuchDrizzle
      createCharByte(1, MuchDrizzle);
      break;
    case 61: case 63: case 80:
      //LightRain
      createCharByte(1, LightRain);
      break;
    case 65: case 67: case 81:
      //HeavyRain
      createCharByte(1, HeavyRain);
      break;
    case 82: case 96: case 99: case 95:
      //FloodStorm
      createCharByte(1, FloodStorm);
      break;
    case 85: case 86: case 71: case 73: case 75: case 77:
      //Snow
      
      createCharByte(1, Snow);
      break;
  }
  if (cursored) {
    lcd.setCursor(16, 1);
    cursored = false;
  } else {
    lcd.setCursor(0, 1);
  }
  lcd.write(byte(1));
}

void Clocked() {
  
  if (seconds() >= BootM) {
    BootM = seconds() + 60;
    now = RTC.now();
    lcd.clear();
  }
  if (UpdatedDate < now.day() && now.hour() >= 10) {
    digitalWrite(6, LOW);
    delay(100);
    digitalWrite(6, HIGH);
    UpdatedDate = now.day();
  }
  
  lcd.setCursor(0, 0);
  switch (now.month()) {
    case 1:
      lcd.print("Jan ");
      break;
    case 2:
      lcd.print("Feb ");
      break;
    case 3:
      lcd.print("Mar ");
      break;
    case 4:
      lcd.print("Apr ");
      break;
    case 5:
      lcd.print("May ");
      break;
    case 6:
      lcd.print("Jun ");
      break;
    case 7:
      lcd.print("Jul ");
      break;
    case 8:
      lcd.print("Aug ");
      break;
    case 9:
      lcd.print("Sep ");
      break;
    case 10:
      lcd.print("Oct ");
      break;
    case 11:
      lcd.print("Nov ");
      break;
    case 12:
      lcd.print("Dec ");
      break;
  }
  lcd.print(now.day());
  lcd.print(", ");
  int dayofweek = weekday(now.year(), now.month(), now.day());
  switch (dayofweek) {
    case 1:
      lcd.print("Monday");
      break;
    case 2:
      lcd.print("Tuesday");
      break;
    case 3:
      lcd.print("Wednesday");
      break;
    case 4:
      lcd.print("Thursday");
      break;
    case 5:
      lcd.print("Friday");
      break;
    case 6:
      lcd.print("Saturday");
      break;
    case 0:
      lcd.print("Sunday");
      break;
  }
  lcd.setCursor(0, 1);
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.day(), DEC);
  lcd.print("  ");

  if (now.hour() > 12) {
    lcd.print(now.hour() - 12);
    lcd.print(':');
    if (now.minute() < 10) {
      lcd.print("0");
    }
    lcd.print(now.minute());
    lcd.print(" PM");
  } else {
    lcd.print(now.hour());
    lcd.print(':');
    if (now.minute() < 10) {
      lcd.print("0");
    }
    lcd.print(now.minute());
    lcd.print(" AM");
  }
  lcd.print(" ");
  lcd.setCursor(15, 1);
  if (status == "N") {
    lcd.write(byte(0));
  } else if (status == "C") {
    lcd.write(byte(1));
  } else if (status == "D") {
    lcd.write(byte(2));
  } else if (status == "R") {
    lcd.write(byte(3));
  } else if (status == "done") {
    //lcd.write(byte(4));
    cursored = true;
    WCode(DailyCode[4]);
  } else if (status == "E") {
    lcd.write(byte(5));
  }
}

int split(String input, char separator, String *list, int max_items) {
  int count = 0;
  int last_index = 0;
  for (int i = 0; i < input.length(); i++) {
    if (input[i] == separator) {
      list[count] = input.substring(last_index, i);
      last_index = i + 1;
      count++;
    }
  }
  list[count] = input.substring(last_index);
  return count + 1;
}

bool Inter = true;
bool Enter = false;
int page = 1;

void MenuControl(int pages) {
  currentStateCLK = digitalRead(CLK);
  if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
    if (digitalRead(DT) != currentStateCLK) {
      counter --;
      currentDir = "CCW";
    } else {
      counter ++;
      currentDir = "CW";
    }
  }
  lastStateCLK = currentStateCLK;
  int btnState = digitalRead(SW);
  if (btnState == LOW) {
    if (millis() - lastButtonPress > 50) {
      Enter = true;
    }
    lastButtonPress = millis();
  }
  if ((counter > 2 || counter < -2) && pages > 1) {
    refreshDisplay = true;
    if (counter > 2) {
      if (selection > 0) {
        selection --;
      }
      if (pages <= 5) {
        if (selection == pages + 1) {
          selection = 1;
        } else if (selection == 0) {
          selection = pages;
        }
      }
    }
    if (counter < -2) {
      if (selection <= pages) {
        selection ++;
      }
      if (pages <= 5) {
        if (selection == pages + 1) {
          selection = 1;
        } else if (selection == 0) {
          selection = pages;
        }
      }
    }
    counter = 0;
  }
  delay(1);
}

void WDesc(int Code) {
  lcd.print(" ");
  switch (Code) {
    //Serial.println("Called WDesc");
    case 0:
      lcd.print("Clear sky");
      break;
    case 1:
      lcd.print("Mainly clear");
      break;
    case 2:
      lcd.print("Partly cloudy");
      break;
    case 3:
      lcd.print("Overcast");
      break;
    case 45:
      lcd.print("Fog");
      break;
    case 48:
      lcd.print("Depositing rime fog");
      break;
    case 51:
      lcd.print("Light drizzle");
      break;
    case 53:
      lcd.print("Moderate drizzle");
      break;
    case 55:
      lcd.print("Dense drizzle");
      break;
    case 56:
      lcd.print("Light freezing drizzle");
      break;
    case 57:
      lcd.print("Dense freezing drizzle");
      break;
    case 61:
      lcd.print("Slight rain");
      break;
    case 63:
      lcd.print("Moderate rain");
      break;
    case 65:
      lcd.print("Heavy rain");
      break;
    case 66:
      lcd.print("Light freezing rain");
      break;
    case 67:
      lcd.print("Heavy freezing rain");
      break;
    case 71:
      lcd.print("Slight snow fall");
      break;
    case 73:
      lcd.print("Moderate snow fall");
      break;
    case 75:
      lcd.print("Heavy snow fall");
      break;
    case 77:
      lcd.print("Snow grains");
      break;
    case 80:
      lcd.print("Slight rain showers");
      break;
    case 81:
      lcd.print("Moderate rain showers");
      break;
    case 82:
      lcd.print("Violent rain showers");
      break;
    case 85:
      lcd.print("Slight snow showers");
      break;
    case 86:
      lcd.print("Heavy snow showers");
      break;
    case 95:
      lcd.print("Slight thunderstorm");
      break;
    case 96:
    case 99:
      lcd.print("Thunderstorm Hail");
      break;
    default:
      lcd.print("Unknown ");
      lcd.print(Code);
      break;
  }
}

/*void displayHour(unsigned long timestamp) {
  // Create a time structure to hold the parsed timestamp
  time_t rawtime = timestamp;
  struct tm *timeinfo;
  timeinfo = localtime(&rawtime);

  // Use the tm_hour field to get the hour in a 12-hour format
  int hour = timeinfo->tm_hour;
  if (hour == 0) {
    hour = 12;
    Serial.print("12");
  } else if (hour > 12) {
    hour -= 12;
    Serial.print(hour);
  } else {
    Serial.print(hour);
  }

  // Use the tm_hour field to determine if it's AM or PM
  if (timeinfo->tm_hour >= 12) {
    Serial.println(" PM");
  } else {
    Serial.println(" AM");
  }
  }*/







void loop() {
  if (screen == "MM") {
    //********************************************************************************************* Main Menu
    MenuControl(3);
    if (refreshDisplay) {
      lcd.clear();
      if (selection == 1) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("-->Back     Temp");
        lcd.setCursor(0, 1);
        lcd.print("     Forcast   ");
      }
      if (selection == 2) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back  -->Temp");
        lcd.setCursor(0, 1);
        lcd.print("     Forcast   ");
      }
      if (selection == 3) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back     Temp");
        lcd.setCursor(0, 1);
        lcd.print("  -->Forcast   ");
      }
      refreshDisplay = false;
    }
    if (Enter) {
      if (selection == 1) {
        screen = "HM";
      }
      if (selection == 2) {
        screen = "TM";
      }
      if (selection == 3) {
        screen = "FM";
      }
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
  } else if (screen == "TM") {
    //********************************************************************************************* Tempurature Menu
    MenuControl(3);
    if (refreshDisplay) {
      lcd.clear();
      if (selection == 1) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("-->Back    Today");
        lcd.setCursor(0, 1);
        lcd.print("    Tommorrow   ");
      }
      if (selection == 2) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back -->Today");
        lcd.setCursor(0, 1);
        lcd.print("    Tommorrow   ");
      }
      if (selection == 3) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back    Today");
        lcd.setCursor(0, 1);
        lcd.print(" -->Tommorrow   ");
      }
      refreshDisplay = false;
    }
    if (Enter) {
      if (selection == 1) {
        screen = "MM";
      }
      if (selection == 2) {
        screen = "TT";
      }
      if (selection == 3) {
        screen = "TW";
      }
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
  } else if (screen == "FM") {
    //********************************************************************************************* Forcast Menu
    MenuControl(3);
    if (refreshDisplay) {
      lcd.clear();
      if (selection == 1) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("-->Back    Daily");
        lcd.setCursor(0, 1);
        lcd.print("     Hourly   ");
      }
      if (selection == 2) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back -->Daily");
        lcd.setCursor(0, 1);
        lcd.print("     Hourly   ");
      }
      if (selection == 3) {
        lcd.setCursor(0, 0);
        //lcd.print("0000000000000000");
        lcd.print("   Back    Daily");
        lcd.setCursor(0, 1);
        lcd.print("  -->Hourly   ");
      }
      refreshDisplay = false;
    }
    if (Enter) {
      if (selection == 1) {
        screen = "MM";
      }
      if (selection == 2) {
        screen = "DW";
      }
      if (selection == 3) {
        screen = "HW";
      }
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
  } else if (screen == "TT") {
    //********************************************************************************************* Tempurature Today
    MenuControl(1);
    if (refreshDisplay) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("     Today      ");
      delay(1000);
      lcd.clear();
      lcd.print("  HIGH    LOW   ");
      lcd.setCursor(2, 1);
      lcd.print(DailyCode[0]);
      lcd.setCursor(10, 1);
      lcd.print(DailyCode[1]);
      /*
         TH = list[0];
        TL = list[1];
        MH = list[2];
        ML = list[3];
      */
      //lcd.print(" 27.00   99.05");
      refreshDisplay = false;
    }
    if (Enter) {
      screen = "TM";
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
  } else if (screen == "TW") {
    //********************************************************************************************* Tempurature Tommorrow
    MenuControl(1);
    if (refreshDisplay) {
      lcd.clear();
      lcd.setCursor(0, 0);
      //lcd.print("0000000000000000");
      lcd.print("   Tommorrow    ");
      delay(1000);
      lcd.clear();
      lcd.print("  HIGH    LOW   ");
      lcd.setCursor(2, 1);
      lcd.print(DailyCode[2]);
      lcd.setCursor(10, 1);
      lcd.print(DailyCode[3]);
      /*
         TH = list[0];
        TL = list[1];
        MH = list[2];
        ML = list[3];
      */
      //lcd.print(" 27.00   99.05");
      refreshDisplay = false;
    }
    if (Enter) {
      screen = "TM";
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
  } else if (screen == "DW") {//daily weather
    //********************************************************************************************* Daily Weather Scrolling
    MenuControl(7);
    if (refreshDisplay) {
      lcd.clear();
      //if (selection == 1) {
      lcd.setCursor(0, 0);

      if (selection == 1) {
        lcd.print("Today,  ");
      } else {
        lcd.print("Future, ");
      }
      int dayofweek = weekday(now.year(), now.month(), (now.day() + (selection - 1)));
      switch (dayofweek) {
        case 1:
          lcd.print("Monday");
          break;
        case 2:
          lcd.print("Tuesday");
          break;
        case 3:
          lcd.print("Wednesday");
          break;
        case 4:
          lcd.print("Thursday");
          break;
        case 5:
          lcd.print("Friday");
          break;
        case 6:
          lcd.print("Saturday");
          break;
        case 0:
          lcd.print("Sunday");
          break;
      }
      lcd.setCursor(0, 1);
      WCode(DailyCode[3 + selection]);
      WDesc(DailyCode[3 + selection]);
      refreshDisplay = false;
    }
    if (Enter) {
      screen = "FM";
      refreshDisplay = true;
      selection = 1;
      Enter = false;
    }
    /*
       TH = list[0];
              TL = list[1];
              MH = list[2];
              ML = list[3];
              //DailyCode = [list[4],list[5],list[6],list[7],list[8],list[9],list[10]];
              //StartEpoch = list[11].toInt();
              //HourlyCode = [list[12],list[13],list[14],list[15],list[16],list[17],list[18],list[19],list[20],list[21], list[22], list[23]];
    */

  } else if (screen == "HW") {//hourly weather
    MenuControl(24);
    if (refreshDisplay) {
      lcd.clear();
      int timestamp = abs(DailyCode[11]) + (3600 * selection);///////////////////////////////////////////////////////////////////////////////////////////////////////////////////UPDATE to include NOW date
      int hour = (timestamp / 3600) % 24;  // Divide timestamp by 3600 (seconds in an hour) and take the remainder to get the hour
      if (hour == 0) {
        hour = 12;  // If hour is 0, set it to 12
      } else if (hour > 12) {
        hour -= 12;  // If hour is greater than 12, subtract 12 to get the 12-hour format
      }
      String hourString = "";  // Convert hour to a string
      if (hour >= 12) {
        hourString = " PM";
      } else {
        hourString = " AM";
      }
      lcd.setCursor(0, 0);
      lcd.print(hour);
      lcd.print(":00 ");
      lcd.print(hourString);
      lcd.setCursor(0, 1);
      WCode(DailyCode[11 + selection]);
      WDesc(DailyCode[11 + selection]);
      refreshDisplay = false;
    }
    if (Enter) {
      Serial.println("MEM: ");
        Serial.println(freeMemory());
      screen = "FM";
      selection = 1;
      refreshDisplay = true;
      Enter = false;
    }
  } else {

    if (clocker) {
      MenuControl(1);
          
            Clocked();
         
        /*digitalWrite(6, LOW);
        delay(500);
        digitalWrite(6, HIGH);*/
      }

    if (Enter && clocker) {
      screen = "MM";
      selection = 1;
      refreshDisplay = true;
      Enter = false;
    } else if (refreshDisplay) {
      lcd.clear();
      now = RTC.now();
      Clocked();
      refreshDisplay = false;
    }
    if (Serial.available() && !clocker) {
      char c = Serial.read();
      if (c == 'C') {
        if (status != "N") {
          status = "N";
          Clocked();
        }
      }
      if (c == 'D') {
        if (status != "C") {
          status = "C";
          Clocked();
        }
      }
      if (c == 'E') {
        if (status != "E") {
          status = "E";
          Clocked();
        }
      }
      if (c == 'L') {
        if (status != "D") {
          digitalWrite(LED_BUILTIN, HIGH);
          status = "D";
          Clocked();
        }
      }
      if (c == '{') {
        if (status != "R") {
          status = "R";
          Clocked();
        }
        inputString = "";

        digitalWrite(LED_BUILTIN, HIGH);
      }

      else if (c != '}') {
        if (c == '&') {
          DailyCode[Iteration] = inputString.toInt();
          inputString = "";
          Iteration++;
          Serial.print("-");
        } else {
          inputString += c;
          Serial.print(c);
        }
      } else {
        // display the second element in the list on the LCD
        //TH = list[0];
        //TL = list[1];
        //MH = list[2];
        //ML = list[3];
        //DailyCode = [list[4],list[5],list[6],list[7],list[8],list[9],list[10]];
        //StartEpoch = list[11].toInt();
        //HourlyCode = [list[12],list[13],list[14],list[15],list[16],list[17],list[18],list[19],list[20],list[21], list[22], list[23]];
        status = "done";
        if (!clocker) {
          Clocked();
        }
        clocker = true;
        now = RTC.now();
        cursored = true;
        inputString = "";
        Iteration = 0;
        digitalWrite(LED_BUILTIN, LOW);
        Serial.println("MEM: ");
        Serial.println(freeMemory());
      }
    }
  }
}
