#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <DHT.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Updater.h>
#include <EEPROM.h>
#include <Adafruit_NeoPixel.h>


#define LED_PIN D1
#define NUM_LEDS 2

Adafruit_NeoPixel leds(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

// Night light settings
int ledMode = 0;            // 0 - static light, 1 - pulsation, 2 - rainbow
int ledBrightness = 100;    // Brightness (0–255)
int ledOnHour = 21;         // Turn-on hour
int ledOffHour = 7;         // Turn-off hour


unsigned long lastEffectUpdate = 0;
int pulseState = 0;
bool ledsOn = false;



#define TFT_CS   15 //D8
#define TFT_RST  12 //D6
#define TFT_DC   16 //D0

#define DHTPIN D2
#define DHTTYPE DHT22

#define EEPROM_SIZE 512

// TFT and sensor
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
DHT dht(DHTPIN, DHTTYPE);

// NTP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000);

// Webserver
ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;

// WiFi and crypto config (default values)
String wifi_ssid = "************"; // SSID YOUR WIFI
String wifi_password = "**********"; // password YOUR WIFI

const char* ap_ssid = "SetupAP";        // SSID 
const char* ap_password = "12345678";   // password

String cryptoList = "bitcoin,ethereum,solana,binancecoin,sui";

 
 float latitude = *********; // Weather location
 float longitude = ********; // Weather location

   String timezoneOffset = "auto";  

   int timezone_offset = 0; 
   
const char* weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const char* serverName = "https://api.coingecko.com/api/v3/simple/price";  // API CoinGecko

String weatherData;

bool showWeather = true;
bool showCrypto = true;
bool sensorRead = false;

float cachedTemperature = NAN;
float cachedHumidity = NAN;

unsigned long lastSwitch = 0;
unsigned long lastWeatherRequest = 0;
const unsigned long weatherInterval = 30000;



// --- EEPROM ---

void saveSettings() {
  EEPROM.begin(EEPROM_SIZE);

  // Save wifi_ssid 
  int len = wifi_ssid.length();
  if (len > 50) len = 50;
  EEPROM.write(0, len);
  for (int i = 0; i < len; i++) {
    EEPROM.write(1 + i, wifi_ssid[i]);
  }

  EEPROM.write(312, ledMode);
  EEPROM.write(313, ledBrightness);
  EEPROM.write(314, ledOnHour);
  EEPROM.write(315, ledOffHour);


  // Save wifi_password 
  len = wifi_password.length();
  if (len > 50) len = 50;
  EEPROM.write(51, len);
  for (int i = 0; i < len; i++) {
    EEPROM.write(52 + i, wifi_password[i]);
  }

  // Save cryptoList 
  len = cryptoList.length();
  if (len > 200) len = 200;
  EEPROM.write(102, len);
  for (int i = 0; i < len; i++) {
    EEPROM.write(103 + i, cryptoList[i]);
  }

  // Save latitude і longitude 
  EEPROM.put(304, latitude);      
  EEPROM.put(304 + sizeof(float), longitude);

  EEPROM.commit();

  Serial.println("Settings saved to EEPROM");
}
void loadSettings() {
  EEPROM.begin(EEPROM_SIZE);

  // Load wifi_ssid
  int len = EEPROM.read(0);
  if (len > 50) len = 50;
  wifi_ssid = "";
  for (int i = 0; i < len; i++) {
    wifi_ssid += char(EEPROM.read(1 + i));
  }

  ledMode = EEPROM.read(312);
  ledBrightness = EEPROM.read(313);
  ledOnHour = EEPROM.read(314);
  ledOffHour = EEPROM.read(315);



  // Load wifi_password
  len = EEPROM.read(51);
  if (len > 50) len = 50;
  wifi_password = "";
  for (int i = 0; i < len; i++) {
    wifi_password += char(EEPROM.read(52 + i));
  }

  // Load cryptoList
  len = EEPROM.read(102);
  if (len > 200) len = 200;
  cryptoList = "";
  for (int i = 0; i < len; i++) {
    cryptoList += char(EEPROM.read(103 + i));
  }

  // Load latitude і longitude
  EEPROM.get(304, latitude);
  EEPROM.get(304 + sizeof(float), longitude);

  Serial.println("Settings loaded from EEPROM:");
  Serial.println("SSID: " + wifi_ssid);
  Serial.println("Password: " + wifi_password);
  Serial.println("CryptoList: " + cryptoList);
  Serial.print("Latitude: ");
  Serial.println(latitude, 6);
  Serial.print("Longitude: ");
  Serial.println(longitude, 6);
}
// --- Web Server ---
// Settings page for editing Wi-Fi and crypto configuration

void handleRoot() {
  String page = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>ESP8266 Settings</title>";
  page += "<style>";
  page += "body { font-family: 'Segoe UI', sans-serif; background: #f4f4f4; color: #333; padding: 20px; }";
  page += ".container { max-width: 500px; margin: auto; background: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }";
  page += "h2 { text-align: center; margin-bottom: 20px; }";
  page += "label { font-weight: bold; display: block; margin-top: 10px; }";
  page += "input[type=text], input[type=password] { width: 100%; padding: 10px; margin-top: 5px; border: 1px solid #ccc; border-radius: 5px; box-sizing: border-box; }";
  page += "input[type=submit] { background-color: #28a745; color: white; border: none; padding: 10px 20px; font-size: 16px; margin-top: 20px; border-radius: 5px; cursor: pointer; width: 100%; }";
  page += "input[type=submit]:hover { background-color: #218838; }";
  page += "a { display: block; margin-top: 20px; text-align: center; color: #007bff; text-decoration: none; }";
  page += "a:hover { text-decoration: underline; }";
  page += "</style></head><body>";
  
  page += "<div class='container'>";
  page += "<h2>ESP8266 Settings</h2>";
  page += "<form action=\"/save\" method=\"POST\">";
  
  page += "<label>WiFi SSID:</label>";
  page += "<input type=\"text\" name=\"ssid\" value=\"" + wifi_ssid + "\">";

  page += "<label>WiFi Password:</label>";
  page += "<input type=\"password\" name=\"password\" value=\"" + wifi_password + "\">";

  page += "<label>Crypto List (comma separated):</label>";
  page += "<input type=\"text\" name=\"crypto\" value=\"" + cryptoList + "\">";

  page += "<label>Latitude:</label>";
  page += "<input type=\"text\" name=\"latitude\" value=\"" + String(latitude, 6) + "\">";

  page += "<label>Longitude:</label>";
  page += "<input type=\"text\" name=\"longitude\" value=\"" + String(longitude, 6) + "\">";


   page += "<h2>Night Light Settings</h2>";
  page += "<label>Mode (0=Static, 1=Pulse, 2=Rainbow):</label>";
  page += "<input type='text' name='ledMode' value='" + String(ledMode) + "'>";

  page += "<label>Brightness (0-255):</label>";
  page += "<input type='text' name='ledBrightness' value='" + String(ledBrightness) + "'>";

  page += "<label>ON Hour (0-23):</label>";
  page += "<input type='text' name='ledOnHour' value='" + String(ledOnHour) + "'>";

  page += "<label>OFF Hour (0-23):</label>";
  page += "<input type='text' name='ledOffHour' value='" + String(ledOffHour) + "'>";

    page += "<input type=\"submit\" value=\"Save\">";
    page += "</form>";


  page += "<a href=\"/update\">Firmware update (OTA)</a>";
  page += "</div></body></html>";

   


  server.send(200, "text/html", page);
}


void handleSave() {
  if (server.hasArg("ssid")) wifi_ssid = server.arg("ssid");
  if (server.hasArg("password")) wifi_password = server.arg("password");
  if (server.hasArg("crypto")) cryptoList = server.arg("crypto");

  if (server.hasArg("latitude")) {
    latitude = server.arg("latitude").toFloat();  
  }
  if (server.hasArg("longitude")) {
    longitude = server.arg("longitude").toFloat(); 
  }

  if (server.hasArg("ledMode")) ledMode = server.arg("ledMode").toInt();
   if (server.hasArg("ledBrightness")) ledBrightness = server.arg("ledBrightness").toInt();

  leds.setBrightness(ledBrightness);   
  leds.show();
  if (server.hasArg("ledOnHour")) ledOnHour = server.arg("ledOnHour").toInt();
  if (server.hasArg("ledOffHour")) ledOffHour = server.arg("ledOffHour").toInt();



  saveSettings();

  server.send(200, "text/html", "<html><body><h2>Settings saved. Device will restart...</h2></body></html>");
  delay(2000);
  ESP.restart();
}
void handleUpdate() {
  if (server.method() == HTTP_GET) {
   
    String html = "<html><body><h1>Firmware Update</h1>"
                  "<form method='POST' action='/update' enctype='multipart/form-data'>"
                  "<input type='file' name='update'>"
                  "<input type='submit' value='Update'>"
                  "</form></body></html>";
    server.send(200, "text/html", html);
  } else if (server.method() == HTTP_POST) {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(0xFFFFFFFF)) { 
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { 
        Serial.printf("Update Success: %u bytes\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  }
}


void setup() {
  Serial.begin(115200);

 

  EEPROM.begin(EEPROM_SIZE);
  loadSettings();
     leds.begin();
     leds.show();


  WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());
  Serial.print("Connecting to WiFi");
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (millis() - startTime > 15000) {
      Serial.println(" Failed to connect");
      Serial.println("Start Access Point...");
  WiFi.softAP(ap_ssid, ap_password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("IP адреса AP: ");
  Serial.println(IP);

  server.on("/", handleRoot);
  server.on("/save", HTTP_POST, handleSave);
  httpUpdater.setup(&server);
  server.begin();
  Serial.println("Web server started");
      break;
    }
}

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi Connected: " + WiFi.localIP().toString());
     httpUpdater.setup(&server);
  } else {
    Serial.println("\nWiFi connection failed.");
  }

 
  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK);

  dht.begin();

  
  timeClient.begin();
  timeClient.update();

  
  getWeather();

 
  server.on("/", handleRoot);
  server.on("/save", HTTP_POST, handleSave);
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();

    handleNightLight();


  unsigned long currentMillis = millis();

  timeClient.update();

  if (currentMillis - lastSwitch > 30000) {
    if (showWeather) {
      showWeather = false;
      showCrypto = true;
    } else if (showCrypto) {
      showCrypto = false;
      sensorRead = false;
    } else {
      showWeather = true;
    }
    tft.fillScreen(ST7735_BLACK);
    lastSwitch = currentMillis;
  }

  if (currentMillis - lastWeatherRequest > weatherInterval) {
    getWeather();
    lastWeatherRequest = currentMillis;
  }

  if (showWeather) {
    parseWeather(weatherData);
  } else if (showCrypto) {
    showCryptoData();
  } else {
    showSensorData();
  }
if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi disconnected, trying to reconnect...");
    WiFi.disconnect();
    WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());
    unsigned long startTime = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - startTime < 15000) {
      delay(500);
      Serial.print(".");
    }
}
  delay(1000);


 
}

void handleNightLight() {
  timeClient.update();

  // Корекція часу
  time_t rawtime = timeClient.getEpochTime();       
  rawtime += timezone_offset * 3600;                
  struct tm* timeinfo = gmtime(&rawtime);           
  int currentHour = timeinfo->tm_hour;              // локальна година

  // Час вмикання/вимикання нічника
  bool shouldBeOn = (ledOnHour < ledOffHour) 
                      ? (currentHour >= ledOnHour && currentHour < ledOffHour)
                      : (currentHour >= ledOnHour || currentHour < ledOffHour);

  if (shouldBeOn != ledsOn) {
    ledsOn = shouldBeOn;
    if (!ledsOn) {
      leds.clear();
      leds.show();
    }
  }

  if (!ledsOn) return;

  // Захист від некоректних значень
  ledBrightness = constrain(ledBrightness, 0, 255);
  leds.setBrightness(ledBrightness); // глобальне обмеження яскравості

  switch (ledMode) {
    case 0: // Статичне світло
      for (int i = 0; i < NUM_LEDS; i++) {
        leds.setPixelColor(i, leds.Color(ledBrightness, ledBrightness, ledBrightness));
      }
      leds.show();
      break;

    case 1: // Пульсація
      {
        unsigned long now = millis();
        if (now - lastEffectUpdate > 20) {
          lastEffectUpdate = now;
          static int pulseVal = 0;
          static int dir = 5;
          pulseVal += dir;
          if (pulseVal >= 255 || pulseVal <= 0) dir = -dir;
          int brightness = map(pulseVal, 0, 255, 0, ledBrightness);
          for (int i = 0; i < NUM_LEDS; i++) {
            leds.setPixelColor(i, leds.Color(brightness, brightness, brightness));
          }
          leds.show();
        }
      }
      break;

    case 2: // Веселка (Rainbow HSV)
      {
        unsigned long now = millis();
        if (now - lastEffectUpdate > 50) {
          lastEffectUpdate = now;
          static uint8_t hue = 0;
          for (int i = 0; i < NUM_LEDS; i++) {
            leds.setPixelColor(i, leds.ColorHSV((hue + i * 30) * 256, 255, ledBrightness));
          }
          hue++;
          leds.show();
        }
      }
      break;
  }
}


void getWeather() {
    if (WiFi.status() == WL_CONNECTED) {
        HTTPClient http;
        WiFiClient client;
 String url = String("http://api.open-meteo.com/v1/forecast?latitude=") 
         + String(latitude, 6) 
         + "&longitude=" + String(longitude, 6) 
         + "&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max,wind_speed_10m_max"
         + "&timezone=" + String(timezoneOffset) 
         + "&wind_speed_unit=ms";
        http.begin(client, url);
        int httpCode = http.GET();
        
        if (httpCode > 0) {
            weatherData = http.getString();
            Serial.println(weatherData);
        } else {
            Serial.println("Error on HTTP request");
        }
        http.end();
    }
}


void parseWeather(String json) {
    DynamicJsonDocument doc(2048);
    DeserializationError error = deserializeJson(doc, json);
    tft.setTextSize(1);
    
    if (error) {
        Serial.println("Failed to parse JSON");
        return;
    }

   
    if (doc.containsKey("utc_offset_seconds")) {
        int offset_seconds = doc["utc_offset_seconds"].as<int>();
        timezone_offset = offset_seconds / 3600;  
        Serial.print("Timezone offset (hours): ");
        Serial.println(timezone_offset);
    } else {
        Serial.println("utc_offset_seconds not found");
    }



    int lineHeight = 22;

    for (int i = 0; i < 7; i++) {
        String date = doc["daily"]["time"][i].as<String>();
        String day = date.substring(8, 10);
        String month = date.substring(5, 7);
        String formattedDate = day + "/" + month;
        
        int weekdayIndex = getWeekday(date);
        float t_max = doc["daily"]["temperature_2m_max"][i].as<float>();
        float t_min = doc["daily"]["temperature_2m_min"][i].as<float>();
        float precip_prob = doc["daily"]["precipitation_probability_max"][i].as<float>();
        float wind_max = doc["daily"]["wind_speed_10m_max"][i].as<float>();

        String dayText = String(weekdays[weekdayIndex]) + " " + formattedDate;
        int dayTextWidth = dayText.length() * 6;
        int centerX = (128 - dayTextWidth) / 2;
        
        tft.setCursor(centerX, 5 + (i * lineHeight));
        tft.setTextColor(ST7735_WHITE);
        tft.print(dayText);

    
        int lineY = 5 + (i * lineHeight);
        int leftMargin = 5;  
        int rightMargin = 5; 

        tft.drawLine(centerX - leftMargin, lineY, centerX - leftMargin, lineY + 6, ST7735_WHITE); 
        tft.drawLine(centerX + dayTextWidth + rightMargin, lineY, centerX + dayTextWidth + rightMargin, lineY + 6, ST7735_WHITE); 
        
        
        int lineX = centerX - leftMargin;  
        int lineLength = 15; 
        tft.drawLine(centerX - leftMargin - lineLength, lineY + 6, centerX - leftMargin, lineY + 6, ST7735_WHITE); 
        tft.drawLine(centerX + dayTextWidth + rightMargin, lineY + 6, centerX + dayTextWidth + rightMargin + lineLength, lineY + 6, ST7735_WHITE); 
        
        String tempText = String(t_min, 0) + "-" + String(t_max, 0) + "C";
        int tempTextWidth = tempText.length() * 6;
        tft.setCursor((120 - tempTextWidth) / 2, 17 + (i * lineHeight));
        tft.setTextColor(ST7735_ORANGE);
        tft.print(tempText);

        String precipText = "P:" + String(precip_prob, 0) + "%";
        String windText = "W:" + String(wind_max, 0) + "m/s";
        
        tft.setCursor(5, 17 + (i * lineHeight));
        tft.setTextColor(ST7735_CYAN);
        tft.print(precipText);
        
        tft.setCursor(128 - windText.length() * 6 - 5, 17 + (i * lineHeight));
        tft.setTextColor(ST7735_MAGENTA);
        tft.print(windText);
    }
}

void showCryptoData() {
    if (WiFi.status() != WL_CONNECTED) return;

    WiFiClientSecure client;
    client.setInsecure();

    HTTPClient http;
    String url = String(serverName) + "?ids=" + cryptoList + "&vs_currencies=usd";

    http.begin(client, url);
    int httpCode = http.GET();

    if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        DynamicJsonDocument doc(2048);
        DeserializationError error = deserializeJson(doc, payload);

        if (error) {
            Serial.println("JSON parse error");
            return;
        }

        tft.fillScreen(ST7735_BLACK);

       
        tft.setTextColor(ST7735_WHITE);
        tft.setTextSize(1);
        tft.setCursor(27, 2);
        tft.print("Crypto Rates");

        int y = 15;
        int index = 0;
        String token;
        String list = cryptoList;

        while ((index = list.indexOf(',')) != -1 || list.length() > 0) {
            if (index != -1) {
                token = list.substring(0, index);
                list = list.substring(index + 1);
            } else {
                token = list;
                list = "";
            }

            float price = doc[token]["usd"] | -1.0;
            String label = token;
            label.toUpperCase();
            if (label.length() > 6) {
           label = label.substring(0, 7); 
           }


          
            tft.fillRect(0, y, 128, 15, ST7735_ORANGE);
            tft.drawLine(0, y + 15, 128, y + 15, ST7735_WHITE);

            
            tft.setCursor(4, y + 3);
            tft.setTextColor(ST7735_WHITE);
            tft.print(label);

            
            tft.setCursor(60, y + 3);
            if (price >= 0) {
                tft.setTextColor(ST7735_BLACK);
                tft.print("$");
                tft.print(price, 2);
            } else {
                tft.setTextColor(ST7735_RED);
                tft.print("N/A");
            }

            y += 16;
            if (y > 145) break;
        }

    } else {
        tft.fillScreen(ST7735_BLACK);
        tft.setTextColor(ST7735_RED);
        tft.setTextSize(1);
        tft.setCursor(10, 40);
        tft.print("HTTP Error: ");
        tft.print(httpCode);
    }

    http.end();
  
  
    delay(29900);
}



void showSensorData() {
    static String lastTime = "";
    static String lastDay = "";
    static float lastTemp = NAN;
    static float lastHum = NAN;

    if (!sensorRead) {
        cachedTemperature = NAN;
        cachedHumidity = NAN;
        for (int i = 0; i < 3; i++) {
            cachedTemperature = dht.readTemperature();
            delay(700);
            cachedHumidity = dht.readHumidity();
            if (!isnan(cachedTemperature) && !isnan(cachedHumidity)) break;
        }
        sensorRead = true;
    }

    String formattedTime = getLocalFormattedTime();
    int currentDay = timeClient.getDay();
    const char* fullWeekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
    String fullDayName = fullWeekdays[currentDay];

    // WiFi
    drawWiFiIcon(110, 5, WiFi.status() == WL_CONNECTED);

   
    tft.fillRect(0, 0, 128, 20, 0xFD20);  
    tft.setCursor(20, 5);
    tft.setTextColor(ST7735_WHITE);
    tft.setTextSize(1);
    tft.print("Weatherstation");

    // Час
    tft.fillRect(10, 25, 108, 30, 0xFD20); 
    tft.drawRect(10, 25, 108, 30, 0xFD20); 
    int timePixelWidth = formattedTime.length() * 12;
    int timeX = (128 - timePixelWidth) / 2;
    tft.setCursor(timeX, 35);
    tft.setTextColor(0xffff); 
    tft.setTextSize(2);
    tft.print(formattedTime);

   
    tft.fillRect(0, 60, 128, 12, ST7735_BLACK);
    tft.setCursor((128 - fullDayName.length() * 6) / 2, 60);
    tft.setTextColor(0xffff); 
    tft.setTextSize(1);
    tft.print(fullDayName);

 
    tft.fillRect(5, 75, 118, 30, 0xFD20); 
    tft.drawRect(5, 75, 118, 30, 0xFD20); 
    tft.setCursor(10, 85);
    tft.setTextColor(0x0000); 
    tft.setTextSize(1);
    tft.print("Temp: ");
    tft.setTextSize(2);
    if (!isnan(cachedTemperature)) {
        tft.print(cachedTemperature, 1);
        tft.print("C ");
    } else {
        tft.setTextColor(ST7735_RED);
        tft.print("Err");
    }

    // Вологість
    tft.fillRect(5, 110, 118, 30, 0xFD20); 
    tft.drawRect(5, 110, 118, 30, 0xFD20); 
    tft.setCursor(10, 120);
    tft.setTextColor(0x0000); 
    tft.setTextSize(1);
    tft.print("Humidity: ");
    tft.setTextSize(2);
    if (!isnan(cachedHumidity)) {
        tft.print(cachedHumidity, 1);
        tft.print("% ");
    } else {
        tft.setTextColor(ST7735_RED);
        tft.print("Err");
    }
    delay(29900);
}


void drawWiFiIcon(int x, int y, bool connected) {
    uint16_t color = connected ? ST7735_GREEN : ST7735_RED;

   
    tft.fillRect(x - 10, y - 10, 20, 20, ST7735_BLACK);

    if (connected) {
        
      
        tft.drawCircle(x, y + 4, 6, color);
        tft.drawCircle(x, y + 4, 3, color);

        
        tft.fillRect(x - 9, y + 4, 18, 10, ST7735_BLACK);

        
        tft.fillCircle(x, y + 7, 2, color);
    } else {
        
        tft.drawLine(x - 6, y - 6, x + 6, y + 6, color);
        tft.drawLine(x - 6, y + 6, x + 6, y - 6, color);
    }
}

int getWeekday(String date) {
    
    int y = date.substring(0, 4).toInt();
    int m = date.substring(5, 7).toInt();
    int d = date.substring(8, 10).toInt();

    // Zeller's congruence 
    if (m < 3) {
        m += 12;
        y -= 1;
    }
    int K = y % 100;
    int J = y / 100;
    int f = d + 13*(m+1)/5 + K + K/4 + J/4 + 5*J;
    int weekday = ((f + 6) % 7); // 0=Saturday ... 6=Friday

   
    weekday = (weekday + 1) % 7;

    return weekday;
}

String getLocalFormattedTime() {
    time_t rawtime = timeClient.getEpochTime();  
    rawtime += timezone_offset * 3600;         
    struct tm* timeinfo = gmtime(&rawtime);      

    char buffer[6];
    snprintf(buffer, sizeof(buffer), "%02d:%02d", timeinfo->tm_hour, timeinfo->tm_min);
    return String(buffer);
}
