#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include <Adafruit_NeoMatrix.h>

// Настройки матрицы
#define PIN 15
#define MATRIX_ROWS 64
#define MATRIX_COLS 8
uint8_t TEXT_BRIGHT = 20;
uint8_t FIRE_BRIGHT = 50;

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_ROWS, MATRIX_COLS, PIN,
  NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT + NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG, NEO_GRB + NEO_KHZ800);

// Цвета для текста
uint16_t BLACK;
uint16_t RED;
uint16_t GREEN;
uint16_t BLUE;
uint16_t YELLOW;
uint16_t WHITE;

// Палитра для пламени
// Палитра пламени (черный → красный → оранжевый → желтый → белый)
const uint32_t firePalette[] = {
  matrix.Color(0, 0, 0),        // Черный
  matrix.Color(180, 0, 0),      // Бордо (Темно-красный)
  matrix.Color(220, 60, 0),     // Красное дерево (Красный)
  matrix.Color(255, 100, 0),    // Яркий оранжевый (Оранжевый)
  matrix.Color(255, 150, 40),   // Бриллиантовый оранжево-желтый (Желтый)
  matrix.Color(255, 200, 150)   // Оранжево-персиковый (теплый белый)
};
const uint8_t paletteSize = 6;  // Цветов в палитре

// Буфер для данных пламени
uint8_t fire[MATRIX_ROWS][MATRIX_COLS];

// Переменные для эффекта
bool showFire = true; // Флаг для отображения огня

void generateFire();
uint32_t getFireColor(uint8_t temperature);


// Настройки BLE
#define NAME_DEVICE "ESP32_WhatsApp_Matrix"
BLEServer* pServer = nullptr;
BLECharacteristic* pTxCharacteristic;
const BLEUUID SERVICE_UUID("45ed0428-820c-4e1f-9dc0-9010e78f4140");
const BLEUUID CHAR_UUID("02049c96-72c5-4a3a-9516-f4d95bdf0d7a");

// Переменные для бегущей строки
String currentMessage = "";
int scrollX = 0;
unsigned long lastScrollTime = 0;
const int SCROLL_DELAY = 70;

// Настройки фильтра
const String TARGET_GROUP = "1";           // Замените на название вашей группы
const int MAX_MESSAGE_LENGTH = 250;        // Максимальная длина сообщения

// Состояние BLE подключения
bool bleConnected = false;
unsigned long lastStatusPrint = 0;
const int STATUS_PRINT_INTERVAL = 2000; // Интервал вывода состояния (мс)

// Разделители сообщений
const char FIELD_SEPARATOR = '\x1E';  // ASCII 30
const char MESSAGE_TERMINATOR = '\x1F'; // ASCII 31

// Буфер для накопления данных
String receivedBuffer = "";
const int MAX_BUFFER_SIZE = 512;

String utf8rus(String source);
void setupBluetooth();

// Класс для обработки событий подключения BLE
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
        bleConnected = true;
        Serial.println("BLE: Устройство подключено");
    }

    void onDisconnect(BLEServer* pServer) {
        bleConnected = false;
        showFire = true; // Включаем огоньпри дисконекте BLE
        Serial.println("BLE: Устройство отключено");
        // Перезапускаем рекламу для возможности повторного подключения
        pServer->startAdvertising();
        Serial.println("BLE: Ожидаем подключения...");
   }
};

class MyCallbacks : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic* pChar) override {
    std::string value = pChar->getValue();
    if (value.empty()) return;

    // Защита от переполнения буфера
    if (receivedBuffer.length() > MAX_BUFFER_SIZE) {
      Serial.println("Сброс буфера из-за переполнения");
      receivedBuffer = "";
    }

    // Добавляем новые данные в буфер
    receivedBuffer += String(value.c_str());
    
    // Обработка всех завершенных сообщений в буфере
    int terminatorPos;
    while ((terminatorPos = receivedBuffer.indexOf(MESSAGE_TERMINATOR)) != -1) {
      // Извлекаем полное сообщение (без терминатора)
      String fullMessage = receivedBuffer.substring(0, terminatorPos);
      
      // Удаляем обработанное сообщение из буфера
      receivedBuffer = receivedBuffer.substring(terminatorPos + 1);
      
      // Разделяем сообщение на компоненты
      int firstSep = fullMessage.indexOf(FIELD_SEPARATOR);
      int secondSep = fullMessage.indexOf(FIELD_SEPARATOR, firstSep + 1);
      
      if (firstSep == -1 || secondSep == -1) {
        Serial.println("Ошибка формата сообщения");
        Serial.print("Получено: ");
        Serial.println(fullMessage);
        continue;
      }
      
      String appName = fullMessage.substring(0, firstSep);
      String title = fullMessage.substring(firstSep + 1, secondSep);
      String text = fullMessage.substring(secondSep + 1);
         
      Serial.println("--- Получено новое уведомление ---");
      Serial.print("Приложение: ");
      Serial.println(appName);
      Serial.print("Заголовок: ");
      Serial.println(title);
      Serial.print("Текст: ");
      Serial.println(text);
      Serial.println("---------------------------------");

      if (appName == "WhatsApp"){
        // Преобразуем каждую часть отдельно
        // appName("WhatsApp"); // Точное название программы
        appName = utf8rus(appName);
        title = utf8rus(title);
        text = utf8rus(text);
        
        // Формируем отображаемое сообщение
        String displayMessage = appName + ": " + title + " - " + text;
      
        // Ограничиваем длину сообщения
        if (displayMessage.length() > MAX_MESSAGE_LENGTH) {
          displayMessage = displayMessage.substring(0, MAX_MESSAGE_LENGTH) + "...";
        }
      
        // Обновляем текущее сообщение для отображения
        currentMessage = displayMessage;
        scrollX = matrix.width(); // Сбрасываем позицию прокрутки
        showFire = false; // Отключаем огонь при получении данных
        Serial.println("Выводим полученное сообщение на матрицу...");
      }
    }
  }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Запуск матрицы ...");

  // Инициализация матрицы
  matrix.begin();
  matrix.clear();
  matrix.setBrightness(FIRE_BRIGHT);
  matrix.setTextWrap(false);
  
  // Инициализация цветов
  BLACK = matrix.Color(0, 0, 0);
  RED = matrix.Color(255, 0, 0);
  GREEN = matrix.Color(0, 255, 0);
  BLUE = matrix.Color(0, 0, 255);
  YELLOW = matrix.Color(255, 255, 0);
  WHITE = matrix.Color(255, 255, 255);
  
  setupBluetooth();
}

void loop() {
  if (showFire) {
    // Режим отображения огня
    matrix.setBrightness(FIRE_BRIGHT);
    generateFire();
    matrix.show();
    delay(80); // Оптимальная задержка для пламени
  } 
  else {
    // Режим бегущей строки
    if (millis() - lastScrollTime > SCROLL_DELAY) {
      matrix.fillScreen(BLACK);
      matrix.setBrightness(TEXT_BRIGHT);
      matrix.setTextColor(BLUE);
      matrix.setCursor(scrollX, 0);
      matrix.print(currentMessage);
      matrix.show();
      
      // Обновление позиции прокрутки
      int textWidth = -6 * currentMessage.length();
        if (--scrollX < textWidth) {
          scrollX = matrix.width();
        }
      lastScrollTime = millis();
    }
  }
}

void setupBluetooth() {
  BLEDevice::init(NAME_DEVICE);
  pServer = BLEDevice::createServer();
  
  // Устанавливаем обработчики подключений
  pServer->setCallbacks(new MyServerCallbacks());
  
  BLEService* pService = pServer->createService(SERVICE_UUID);
  
  pTxCharacteristic = pService->createCharacteristic(
    CHAR_UUID,
    BLECharacteristic::PROPERTY_WRITE
  );

  pTxCharacteristic->setCallbacks(new MyCallbacks());
  pService->start();

  BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  
  Serial.println("BLE: Ожидаем подключения...");
}

// === функция русификации шрифта === //
String utf8rus(String source) {
  int i, k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = -1;
  while (i < k) { 
    delay(10); 
    n = source[++i]; 
    if (n >= 0xBF) {
      switch (n) {
        case 0xD0: {
            n = source[++i];
            if (n == 0x81) { n = 0xA8; break; }
            if (n >= 0x90 && n <= 0xBF) n = n + 0x2F;
            break;
          }
        case 0xD1: {
            n = source[++i];
            if (n == 0x91) { n = 0xB7; break; }
            if (n >= 0x80 && n <= 0x8F) n = n + 0x6F;
            break;
          }
      }
    }
    m[0] = n; 
    target += String(m);
  }
  return target;
}

// Генерация эффекта пламени
void generateFire() {
  // Шаг 1: Создаем "искры" в основании (нижняя строка)
  for (int x = 0; x < MATRIX_ROWS; x++) {
    if (random(100) < 30) { // Вероятность появления искры
      fire[x][MATRIX_COLS - 1] = random(180, 255);
    } else {
      fire[x][MATRIX_COLS - 1] = 0;
    }
  }

  // Шаг 2: Распространение пламени вверх
  for (int y = 0; y < MATRIX_COLS - 1; y++) {
    for (int x = 0; x < MATRIX_ROWS; x++) {
      // Усреднение температуры с соседями
      int avg = (
        fire[(x - 1 + MATRIX_ROWS) % MATRIX_ROWS][y + 1] +
        fire[x][y + 1] +
        fire[(x + 1) % MATRIX_ROWS][y + 1] +
        fire[x][(y + 2) % MATRIX_COLS]
      ) / 4;
      
      // Затухание и случайные флуктуации
      fire[x][y] = max(0, avg - int(random(0, 20)));
    }
  }

  // Шаг 3: Визуализация
  for (int y = 0; y < MATRIX_COLS; y++) {
    for (int x = 0; x < MATRIX_ROWS; x++) {
      uint8_t temp = fire[x][y];
      uint32_t color = getFireColor(temp);
      matrix.drawPixel(x, y, color);
    }
  }
}

// Получение цвета из палитры по "температуре"
uint32_t getFireColor(uint8_t temperature) {
  if (temperature < 50) return firePalette[0];
  if (temperature < 90) return firePalette[1];
  if (temperature < 130) return firePalette[2];
  if (temperature < 180) return firePalette[3];
  if (temperature < 220) return firePalette[4];
  return firePalette[5];
}