#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h> // Используем библиотеку для ST7735
#include <ESP8266WebServer.h>
#include <pgmspace.h> // Для хранения данных в памяти программы
#include <time.h>

// Настройки для RGB-дисплея
#define TFT_CS     D1
#define TFT_RST    D2
#define TFT_DC     D3

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// Настройки WiFi
const char* ssid = "Beeline_2G_A7F18A"; // Имя сети WiFi
const char* password = "iD2tAXJN3e";    // Пароль от WiFi

// Настройки NTP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 10800, 60000); // Смещение для Москвы +10800 секунд (3 часа)

// Настройки зуммера
const int buzzerPin = 14; // Пин, к которому подключён зуммер (GPIO14)

// Настройки веб-сервера
ESP8266WebServer server(80);

// Переменные для хранения времени будильника
int alarmHour = -1;
int alarmMinute = -1;

// IP-адрес сервера
String serverIP = "";

// Таймеры для ненавязчивого обновления дисплея и проверки будильника
unsigned long previousDisplayMillis = 0;
const unsigned long displayInterval = 50; // Более частое обновление дисплея

// Флаг для предотвращения многократных срабатываний будильника в одну секунду
bool alarmTriggered = false;

// Структура для капель дождя
struct Raindrop {
    int x;
    int y;
};

// Параметры для дождя
const int maxDrops = 45; // Максимальное количество капель
Raindrop drops[maxDrops];

// Заглушка для изображения (укажите свои данные изображения здесь)
const unsigned char myImage[] PROGMEM = {
    // Замените данными изображения
    // Пример: 0xFF, 0xFF, 0xFF, ... и т. д.
};

const int imageWidth = 64;  // Ширина изображения
const int imageHeight = 64; // Высота изображения

// Функция для форматирования чисел с ведущими нулями
String formatTwoDigits(int digits) {
    if (digits < 10) {
        return "0" + String(digits);
    }
    return String(digits);
}

// HTML и CSS для веб-интерфейса
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP8266 Clock</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body {
      background-color: #2b2b2b;
      color: #e0e0e0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      text-align: center;
      padding: 40px;
    }
    .container {
      background-color: #3c3c3c;
      padding: 20px;
      border-radius: 10px;
      display: inline-block;
    }
    h1 {
      color: #f2f2f7;
    }
    .time {
      font-size: 2em;
      margin: 20px 0;
    }
    .date {
      font-size: 1.5em;
      margin-bottom: 30px;
    }
    input[type=number] {
      width: 60px;
      padding: 10px;
      margin: 5px;
      border: 1px solid #555555;
      border-radius: 5px;
      background-color: #4a4a4a;
      color: #e0e0e0;
      text-align: center;
      font-size: 1em;
    }
    input[type=submit] {
      padding: 10px 20px;
      margin-top: 10px;
      border: none;
      border-radius: 5px;
      background-color: #007aff;
      color: white;
      font-size: 1em;
      cursor: pointer;
    }
    input[type=submit]:hover {
      background-color: #0051a8;
    }
    .ip-address {
      margin-top: 20px;
      font-size: 0.9em;
      color: #a0a0a0;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>ESP8266 Clock</h1>
    <div class="time">--:--:--</div>
    <div class="date">--/--/----</div>
    <h2>Set Alarm</h2>
    <form action="/setAlarm" method="get">
      <input type="number" name="hour" min="0" max="23" placeholder="HH" required> :
      <input type="number" name="minute" min="0" max="59" placeholder="MM" required><br><br>
      <input type="submit" value="Set Alarm">
    </form>
  </div>
  <div class="ip-address">Server IP: --.--.--.--</div>
  
  <script>
    function updateTime() {
      fetch('/time')
        .then(response => response.json())
        .then(data => {
          document.querySelector('.time').innerText = data.time;
          document.querySelector('.date').innerText = data.date;
          document.querySelector('.ip-address').innerText = "Server IP: " + data.ip;
        });
    }
    setInterval(updateTime, 1000);
    window.onload = updateTime;
  </script>
</body>
</html>
)rawliteral";

// Функция для инициализации капель дождя
void initializeRain() {
    for (int i = 0; i < maxDrops; i++) {
        drops[i].x = random(0, 80); // Ширина дисплея 80
        drops[i].y = random(0, 160); // Высота дисплея 160
    }
}

// Обновление движения дождя
void updateRain() {
    for (int i = 0; i < maxDrops; i++) {
        drops[i].y += 5; // Скорость падения

        // Заново инициализируем капли, если они ушли за экран
        if (drops[i].y >= 160) { // Высота дисплея 160
            drops[i].x = random(0, 80); // Ширина дисплея 80
            drops[i].y = 0;
        }
    }
}

// Функция для отображения дождя на дисплее
void drawRain() {
    for (int i = 0; i < maxDrops; i++) {
        tft.drawLine(drops[i].x, drops[i].y, drops[i].x, drops[i].y + 3, ST7735_WHITE);
    }
}

// Основная функция для отображения сообщения о срабатывании будильника
void displayAlarmTriggered(String currentTime) {
    tft.fillScreen(ST7735_BLACK);
    tft.setTextSize(1);
    tft.setTextColor(ST7735_WHITE);
    tft.setCursor(10, 10);
    tft.println("Alarm Triggered!");
    tft.print("Time: ");
    tft.println(currentTime);
}

// Функция для отображения сообщения о сбросе будильника
void displayAlarmReset() {
    tft.fillScreen(ST7735_BLACK);
    tft.setTextSize(1);
    tft.setTextColor(ST7735_WHITE);
    tft.setCursor(10, 10);
    tft.println("Alarm Reset");
    tft.println("No Active Alarm");
}

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

    pinMode(buzzerPin, OUTPUT);
    digitalWrite(buzzerPin, LOW);

    tft.initR(INITR_BLACKTAB); // Инициализация дисплея
    tft.setRotation(3); // Установка поворота на 90° влево
    tft.fillScreen(ST7735_BLACK); // Заливка экрана черным цветом

    // Центрирование координат для изображения
    int xCenter = (tft.width() - imageWidth) / 2;
    int yCenter = (tft.height() - imageHeight) / 2;

    // Загрузка изображения в дисплей
    tft.drawBitmap(xCenter, yCenter, myImage, imageWidth, imageHeight, ST7735_WHITE);

    // Заставка при старте
    tft.setTextColor(ST7735_WHITE);
    tft.setTextSize(2);
    tft.setCursor(10, 10);
    tft.println("Starting device...");
    delay(1000);
    
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
        tft.setCursor(10, 30);
        tft.print("Connecting to WiFi");
    }

    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    serverIP = WiFi.localIP().toString();

    // Отображение IP адреса
    tft.setCursor(10, 50);
    tft.print("IP: ");
    tft.println(serverIP);
    delay(2000);

    digitalWrite(buzzerPin, HIGH);
    delay(200);
    digitalWrite(buzzerPin, LOW);
    delay(200);

    timeClient.begin();
    timeClient.update();
    server.on("/", []() {
        server.send_P(200, "text/html", index_html);
    });
    server.on("/time", []() {
        unsigned long epochTime = timeClient.getEpochTime();
        time_t rawTime = epochTime;
        struct tm* ptm = gmtime(&rawTime);

        String formattedDate = String(ptm->tm_mday) + "/" +
                               String(ptm->tm_mon + 1) + "/" +
                               String(ptm->tm_year + 1900);
        String formattedTime = formatTwoDigits(ptm->tm_hour) + ":" +
                               formatTwoDigits(ptm->tm_min) + ":" +
                               formatTwoDigits(ptm->tm_sec);

        String json = "{\"date\":\"" + formattedDate + "\",\"time\":\"" + formattedTime + "\",\"ip\":\"" + serverIP + "\"}";
        server.send(200, "application/json", json);
    });
    server.on("/setAlarm", []() {
        if (server.hasArg("hour") && server.hasArg("minute")) {
            alarmHour = server.arg("hour").toInt();
            alarmMinute = server.arg("minute").toInt();
            String message = "Alarm set for " + String(formatTwoDigits(alarmHour)) + ":" + String(formatTwoDigits(alarmMinute));
            server.send(200, "text/html", "<html><body><script>alert('" + message + "'); window.location.href='/';</script></body></html>");
            Serial.println(message);
        } else {
            server.send(400, "text/html", "Bad Request");
            Serial.println("Bad Request: Missing hour or minute");
        }
    });
    server.onNotFound([]() {
        server.send(404, "text/plain", "404: Not found");
    });
    server.begin();
    Serial.println("HTTP server started");

    initializeRain();
}

void loop() {
    server.handleClient();
    timeClient.update();

    unsigned long epochTime = timeClient.getEpochTime();
    time_t rawTime = epochTime;
    struct tm* ptm = gmtime(&rawTime);
    String formattedTime = formatTwoDigits(ptm->tm_hour) + ":" +
                           formatTwoDigits(ptm->tm_min);
    String formattedSeconds = formatTwoDigits(ptm->tm_sec);

    if (millis() - previousDisplayMillis >= displayInterval) {
        previousDisplayMillis = millis();

        tft.fillScreen(ST7735_BLACK); // Очистка экрана перед отрисовкой

        // Отрисовка часов и минут
        tft.setTextSize(3); // Увеличенные часы и минуты
        tft.setTextColor(ST7735_WHITE);
        tft.setCursor(10, 10);
        tft.print(formattedTime);

        // Отрисовка секунд
        tft.setTextSize(1); // Уменьшенные секунды
        tft.setCursor(10, 50); // Подстройка позиции
        tft.print(":" + formattedSeconds);

        // Обновление и отрисовка дождя
        updateRain();
        drawRain();
    }

    if (alarmHour != -1 && alarmMinute != -1) {
        if (ptm->tm_hour == alarmHour && ptm->tm_min == alarmMinute && ptm->tm_sec == 0) {
            if (!alarmTriggered) {
                digitalWrite(buzzerPin, HIGH);
                Serial.println("Alarm Triggered!");
                displayAlarmTriggered(formattedTime + ":" + formattedSeconds);
                alarmTriggered = true;
            }
        } else {
            alarmTriggered = false;
        }
    }
} код работает не корректно изображение выходит за пределы дисплея