#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

// Wi-Fi credentials (REPLACE THESE WITH YOUR ACTUAL CREDENTIALS)
const char *ssid1 = "Wifi name";  // <--- REPLACE THIS
const char *password1 = "Wifi password"; // <--- REPLACE THIS
const char *ssid2 = "Secondary wifi name";  // <--- REPLACE THIS (Optional, but recommended)
const char *password2 = "Secondary wifi password"; // <--- REPLACE THIS (Optional, but recommended)

const int timeOffset = 19800;  // <--- REPLACE THIS WITH YOUR TIME ZONE OFFSET IN SECONDS

// NTP Client setup
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "time.google.com");

// LCD instance pointer (will be initialized in setup)
LiquidCrystal_I2C* lcd;

// Common I2C addresses for LCD displays
const uint8_t LCD_ADDRESSES[] = {0x27, 0x3F};

// Custom character arrays for classic serif font
const PROGMEM uint8_t classic_serif_00[8] = {B11111,B11111,B11000,B00000,B00000,B00000,B00000,B00000};
const PROGMEM uint8_t classic_serif_01[8] = {B00000,B00000,B00000,B00000,B00000,B11000,B11111,B11111};
const PROGMEM uint8_t classic_serif_02[8] = {B11111,B00011,B00011,B00011,B00011,B00011,B00011,B11111};
const PROGMEM uint8_t classic_serif_03[8] = {B11111,B11000,B11000,B11000,B11000,B11000,B11000,B11111};
const PROGMEM uint8_t classic_serif_04[8] = {B11111,B00000,B00000,B00000,B00000,B00000,B11111,B11111};
const PROGMEM uint8_t classic_serif_05[8] = {B11000,B11000,B11000,B11000,B11000,B11000,B11000,B11000};
const PROGMEM uint8_t classic_serif_06[8] = {B11111,B11111,B00000,B00000,B00000,B00000,B00000,B11111};
const PROGMEM uint8_t classic_serif_07[8] = {B11000,B11000,B11000,B11000,B11000,B11000,B11000,B11111};

const uint8_t classic_serif_digits[10][4] = {
  {254,254,3,2}, {254,5,254,5}, {6,2,3,4}, {0,2,1,2}, {7,1,254,5},
  {3,6,4,2}, {3,6,3,2}, {0,2,254,5}, {3,2,3,2}, {3,2,4,2}
};

// Month names array
const char* months[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", 
                       "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};

bool initializeLCD() {
  Wire.begin();
  for (uint8_t address : LCD_ADDRESSES) {
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {
      lcd = new LiquidCrystal_I2C(address, 16, 4);
      lcd->init();
      lcd->backlight();
      Serial.printf("LCD found at address 0x%02X\n", address);
      return true;
    }
  }
  Serial.println("No LCD found!");
  return false;
}

void setup() {
  Serial.begin(115200);
  
  if (!initializeLCD()) {
    while (1) {
      delay(1000);
      Serial.println("LCD initialization failed!");
    }
  }
  
  set_font_classic_serif();
  connectToWiFi();
  timeClient.begin();
  timeClient.setTimeOffset(timeOffset);
}

void loop() {
  timeClient.update();
  displayTimeAndDate();
  delay(1000);
}

void connectToWiFi() {
  lcd->clear();
  lcd->setCursor(0, 0);
  lcd->print("Connecting to");
  lcd->setCursor(0, 1);
  lcd->print("Wi-Fi...");

  WiFi.begin(ssid1, password1);
  unsigned long startTime = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
    delay(500);
    lcd->print(".");
  }

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Failed to connect to " + String(ssid1));
    lcd->clear();
    lcd->print("Connecting to");
    lcd->setCursor(0, 1);
    lcd->print("Wi-Fi (2nd)...");
    WiFi.begin(ssid2, password2);
    startTime = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
      delay(500);
      lcd->print(".");
    }
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("Failed to connect to " + String(ssid2));
      lcd->clear();
      lcd->print("Wi-Fi Connection");
      lcd->setCursor(0, 1);
      lcd->print("Failed!");
      while (true) {}
    }
  }

  Serial.println("\nWiFi connected to: " + WiFi.SSID());
  lcd->clear();
  lcd->print("Connected to:");
  lcd->setCursor(0, 1);
  lcd->print(WiFi.SSID());
  delay(5000);
}

void displayTimeAndDate() {
  lcd->clear();
  
  // Get current time components
  int hour = timeClient.getHours();
  int minute = timeClient.getMinutes();
  int second = timeClient.getSeconds();
  
  // Get current date
  time_t epochTime = timeClient.getEpochTime();
  struct tm *ptm = gmtime ((time_t *)&epochTime);
  int day = ptm->tm_mday;
  int month = ptm->tm_mon;
  int year = ptm->tm_year + 1900;
  
  // Display time in top two rows
  String ampm = hour >= 12 ? "PM" : "AM";
  int hour12 = (hour > 12) ? hour - 12 : hour;
  hour12 = (hour12 == 0) ? 12 : hour12;
  
  displayNumber(0, 0, hour12);
  lcd->setCursor(4, 0); lcd->write(254);
  lcd->setCursor(4, 1); lcd->write(254);
  displayNumber(5, 0, minute);
  lcd->setCursor(9, 0); lcd->write(254);
  lcd->setCursor(9, 1); lcd->write(254);
  displayNumber(10, 0, second);
  lcd->setCursor(14, 0);
  lcd->print(ampm);
  
  // Display date in bottom two rows
  displayNumber(0, 2, day);
  lcd->setCursor(4, 2);
  lcd->print(months[month]);
  displayNumber(8, 2, year/100);  // Display first two digits of year
  displayNumber(12, 2, year%100); // Display last two digits of year
}

void displayNumber(int col, int row, int number) {
  int tens = number / 10;
  int ones = number % 10;
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      lcd->setCursor(col + j, row + i);
      lcd->write(classic_serif_digits[tens][i * 2 + j]);
    }
  }
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      lcd->setCursor(col + 2 + j, row + i);
      lcd->write(classic_serif_digits[ones][i * 2 + j]);
    }
  }
}

void set_font_classic_serif() {
  createCustomChar(0, classic_serif_00);
  createCustomChar(1, classic_serif_01);
  createCustomChar(2, classic_serif_02);
  createCustomChar(3, classic_serif_03);
  createCustomChar(4, classic_serif_04);
  createCustomChar(5, classic_serif_05);
  createCustomChar(6, classic_serif_06);
  createCustomChar(7, classic_serif_07);
}

void createCustomChar(uint8_t location, const uint8_t charmap[]) {
  uint8_t temp[8];
  for (int i = 0; i < 8; i++) {
    temp[i] = pgm_read_byte(&charmap[i]);
  }
  lcd->createChar(location, temp);
}