#include <Arduino.h>
#include <Wire.h>
#include <lvgl.h>
#include <Arduino_GFX_Library.h>
#include <TouchDrvCSTXXX.hpp>
#include "lv_font_ru.h"

#define PIN_LCD_CS    12
#define PIN_LCD_SCLK  38
#define PIN_LCD_D0     4
#define PIN_LCD_D1     5
#define PIN_LCD_D2     6
#define PIN_LCD_D3     7
#define PIN_LCD_RST    2
#define PIN_I2C_SDA   15
#define PIN_I2C_SCL   14
#define PIN_TP_INT    11

#define DISP_W 480
#define DISP_H 480
#define LVGL_TICK_MS 2
#define TEST_VERSION "v1-ru-font"

static lv_disp_draw_buf_t gDrawBuf;
static lv_color_t *gBuf1 = nullptr;
static lv_color_t *gBuf2 = nullptr;
static lv_obj_t *gStatusLabel = nullptr;
static lv_obj_t *gTouchLabel = nullptr;
static uint32_t gClickCount = 0;
static bool gTouchActive = false;
static int16_t gLastX = -1;
static int16_t gLastY = -1;

Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
    PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
Arduino_CO5300 *gfx = new Arduino_CO5300(
    gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
TouchDrvCST92xx gTouch;

static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
  uint32_t w = area->x2 - area->x1 + 1;
  uint32_t h = area->y2 - area->y1 + 1;
#if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
#else
  gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
#endif
  lv_disp_flush_ready(disp);
}

static void lvglTick(void *arg) {
  LV_UNUSED(arg);
  lv_tick_inc(LVGL_TICK_MS);
}

static void updateTouchLabel() {
  if (!gTouchLabel) {
    return;
  }

  char text[96];
  if (gTouchActive) {
    snprintf(text, sizeof(text), "Касание: x=%d y=%d", gLastX, gLastY);
  } else {
    snprintf(text, sizeof(text), "Касание: нет x=%d y=%d", gLastX, gLastY);
  }
  lv_label_set_text(gTouchLabel, text);
}

static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
  LV_UNUSED(indevDrv);
  int16_t x = 0;
  int16_t y = 0;
  bool touching = gTouch.getPoint(&x, &y, 1) > 0;

  if (touching) {
    gTouchActive = true;
    gLastX = x;
    gLastY = y;
    data->state = LV_INDEV_STATE_PR;
    data->point.x = x;
    data->point.y = y;
  } else {
    gTouchActive = false;
    data->state = LV_INDEV_STATE_REL;
    data->point.x = gLastX >= 0 ? gLastX : 0;
    data->point.y = gLastY >= 0 ? gLastY : 0;
  }

  updateTouchLabel();
}

static void buttonEventCb(lv_event_t *event) {
  if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
    return;
  }

  const char *name = (const char *)lv_event_get_user_data(event);
  gClickCount++;

  char text[128];
  snprintf(text, sizeof(text), "Нажато: %s (%lu)", name, (unsigned long)gClickCount);
  lv_label_set_text(gStatusLabel, text);
  Serial.println(text);
}

static lv_obj_t *makeButton(lv_obj_t *parent,
                            const char *labelText,
                            lv_coord_t x,
                            lv_coord_t y,
                            lv_coord_t w,
                            lv_coord_t h,
                            uint32_t bgColor) {
  lv_obj_t *btn = lv_btn_create(parent);
  lv_obj_set_size(btn, w, h);
  lv_obj_set_pos(btn, x, y);
  lv_obj_set_style_radius(btn, 16, 0);
  lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
  lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
  lv_obj_set_style_border_width(btn, 2, 0);
  lv_obj_set_style_border_color(btn, lv_color_hex(0x6F8BA4), 0);
  lv_obj_set_style_shadow_width(btn, 0, 0);
  lv_obj_set_style_pad_all(btn, 4, 0);
  lv_obj_add_event_cb(btn, buttonEventCb, LV_EVENT_CLICKED, (void *)labelText);

  lv_obj_t *label = lv_label_create(btn);
  lv_label_set_text(label, labelText);
  lv_obj_set_width(label, w - 12);
  lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
  lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
  lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
  lv_obj_set_style_text_font(label, &lv_font_ru_18, 0);
  lv_obj_center(label);
  return btn;
}

static void createUi() {
  lv_obj_t *screen = lv_scr_act();
  lv_obj_set_style_bg_color(screen, lv_color_hex(0x08131E), 0);
  lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
  lv_obj_set_style_pad_all(screen, 0, 0);
  lv_obj_set_scrollbar_mode(screen, LV_SCROLLBAR_MODE_OFF);

  lv_obj_t *title = lv_label_create(screen);
  lv_label_set_text(title, "Русский тест LVGL");
  lv_obj_set_style_text_font(title, &lv_font_ru_24, 0);
  lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
  lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);

  lv_obj_t *version = lv_label_create(screen);
  lv_label_set_text(version, "Версия: " TEST_VERSION);
  lv_obj_set_style_text_font(version, &lv_font_ru_18, 0);
  lv_obj_set_style_text_color(version, lv_color_hex(0xD8E0EA), 0);
  lv_obj_align(version, LV_ALIGN_TOP_MID, 0, 42);

  lv_obj_t *hint = lv_label_create(screen);
  lv_label_set_text(hint, "Проверка кириллицы, длинных слов, кнопок и нажатий.");
  lv_obj_set_width(hint, 436);
  lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
  lv_obj_set_style_text_font(hint, &lv_font_ru_18, 0);
  lv_obj_set_style_text_color(hint, lv_color_hex(0xD8E0EA), 0);
  lv_obj_align(hint, LV_ALIGN_TOP_MID, 0, 72);

  makeButton(screen, "Статус", 22, 126, 136, 62, 0x355C7D);
  makeButton(screen, "Подключение", 172, 126, 136, 62, 0x2A9D8F);
  makeButton(screen, "Кошелёк", 322, 126, 136, 62, 0x457B9D);

  makeButton(screen, "Запросы", 22, 204, 136, 62, 0xE76F51);
  makeButton(screen, "Настройки", 172, 204, 136, 62, 0x8D5A97);
  makeButton(screen, "Регистрация", 322, 204, 136, 62, 0x6A994E);

  makeButton(screen, "Разрешить", 22, 282, 136, 62, 0xBC6C25);
  makeButton(screen, "Отклонить", 172, 282, 136, 62, 0xB56576);
  makeButton(screen, "Назад", 322, 282, 136, 62, 0x6C757D);

  makeButton(screen, "Проверка переноса русского текста", 22, 360, 436, 64, 0x1D6F42);

  lv_obj_t *statusPanel = lv_obj_create(screen);
  lv_obj_set_size(statusPanel, 436, 24);
  lv_obj_set_pos(statusPanel, 22, 428);
  lv_obj_set_style_radius(statusPanel, 10, 0);
  lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162435), 0);
  lv_obj_set_style_bg_opa(statusPanel, LV_OPA_COVER, 0);
  lv_obj_set_style_border_width(statusPanel, 1, 0);
  lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x4D6986), 0);
  lv_obj_set_style_pad_all(statusPanel, 2, 0);

  gStatusLabel = lv_label_create(statusPanel);
  lv_label_set_text(gStatusLabel, "Нажато: ничего");
  lv_obj_set_style_text_font(gStatusLabel, &lv_font_ru_18, 0);
  lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
  lv_obj_center(gStatusLabel);

  lv_obj_t *touchPanel = lv_obj_create(screen);
  lv_obj_set_size(touchPanel, 436, 24);
  lv_obj_set_pos(touchPanel, 22, 454);
  lv_obj_set_style_radius(touchPanel, 10, 0);
  lv_obj_set_style_bg_color(touchPanel, lv_color_hex(0x162435), 0);
  lv_obj_set_style_bg_opa(touchPanel, LV_OPA_COVER, 0);
  lv_obj_set_style_border_width(touchPanel, 1, 0);
  lv_obj_set_style_border_color(touchPanel, lv_color_hex(0x4D6986), 0);
  lv_obj_set_style_pad_all(touchPanel, 2, 0);

  gTouchLabel = lv_label_create(touchPanel);
  lv_label_set_text(gTouchLabel, "Касание: нет x=-1 y=-1");
  lv_obj_set_style_text_font(gTouchLabel, &lv_font_ru_18, 0);
  lv_obj_set_style_text_color(gTouchLabel, lv_color_hex(0xD8E0EA), 0);
  lv_obj_center(gTouchLabel);
}

void setup() {
  Serial.begin(115200);
  Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);

  gfx->begin();
  gBus->writeC8D8(0x36, 0xA0);
  gfx->setBrightness(220);
  gfx->fillScreen(0x0000);

  gTouch.setPins(PIN_TP_INT, -1);
  gTouch.begin(Wire, CST92XX_SLAVE_ADDRESS, PIN_I2C_SDA, PIN_I2C_SCL);
  gTouch.setMaxCoordinates(DISP_W, DISP_H);
  gTouch.setSwapXY(true);
  gTouch.setMirrorXY(true, false);

  lv_init();

  uint32_t screenWidth = gfx->width();
  uint32_t screenHeight = gfx->height();
  uint32_t bufPixels = screenWidth * 40;
  gBuf1 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
  gBuf2 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);

  lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, bufPixels);

  static lv_disp_drv_t dispDrv;
  lv_disp_drv_init(&dispDrv);
  dispDrv.hor_res = screenWidth;
  dispDrv.ver_res = screenHeight;
  dispDrv.flush_cb = lvglFlushCb;
  dispDrv.draw_buf = &gDrawBuf;
  lv_disp_drv_register(&dispDrv);

  static lv_indev_drv_t indevDrv;
  lv_indev_drv_init(&indevDrv);
  indevDrv.type = LV_INDEV_TYPE_POINTER;
  indevDrv.read_cb = lvglTouchRead;
  lv_indev_drv_register(&indevDrv);

  const esp_timer_create_args_t tickArgs = {
      .callback = &lvglTick,
      .name = "lvgl_tick"};
  esp_timer_handle_t tickTimer = nullptr;
  esp_timer_create(&tickArgs, &tickTimer);
  esp_timer_start_periodic(tickTimer, LVGL_TICK_MS * 1000);

  createUi();
  Serial.println("Русский LVGL font test ready: " TEST_VERSION);
}

void loop() {
  lv_timer_handler();
  delay(5);
}
