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

#define EXAMPLE_LVGL_TICK_PERIOD_MS 2

static lv_disp_draw_buf_t gDrawBuf;
static lv_color_t *gBuf1 = nullptr;
static lv_color_t *gBuf2 = nullptr;
static lv_obj_t *gRawLabel = nullptr;
static lv_obj_t *gEventLabel = nullptr;
static lv_obj_t *gMarker = nullptr;

static TouchDrvCST92xx gTouch;
static int16_t gTouchX[5];
static int16_t gTouchY[5];
static bool gTouchPressed = false;
static uint32_t gRawTouchCount = 0;
static uint32_t gLvglButtonCount = 0;

Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
    LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
Arduino_CO5300 *gfx = new Arduino_CO5300(
    gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);

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(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
  LV_UNUSED(indevDrv);
  uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
  if (touched > 0) {
    data->state = LV_INDEV_STATE_PR;
    data->point.x = gTouchX[0];
    data->point.y = gTouchY[0];
    return;
  }
  data->state = LV_INDEV_STATE_REL;
}

static void bigButtonEventCb(lv_event_t *event) {
  if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
    return;
  }
  gLvglButtonCount++;
  String status = "LVGL button clicked: ";
  status += String(gLvglButtonCount);
  lv_label_set_text(gEventLabel, status.c_str());
  Serial.println(status);
}

static void updateRawTouchState() {
  uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
  if (touched > 0) {
    gTouchPressed = true;
    gRawTouchCount++;

    String line1 = "RAW pressed";
    String line2 = "x=" + String(gTouchX[0]) + " y=" + String(gTouchY[0]) + " n=" + String(gRawTouchCount);
    String text = line1 + "\n" + line2;
    lv_label_set_text(gRawLabel, text.c_str());

    int markerX = gTouchX[0] - 8;
    int markerY = gTouchY[0] - 8;
    if (markerX < 0) markerX = 0;
    if (markerY < 0) markerY = 0;
    lv_obj_set_pos(gMarker, markerX, markerY);
    lv_obj_clear_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
    return;
  }

  if (gTouchPressed) {
    gTouchPressed = false;
    lv_label_set_text(gRawLabel, "RAW released\nTouch ended");
    lv_obj_add_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
  }
}

static void createUi() {
  lv_obj_t *screen = lv_scr_act();
  lv_obj_set_style_bg_color(screen, lv_color_hex(0x000000), 0);
  lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);

  lv_obj_t *title = lv_label_create(screen);
  lv_label_set_text(title, "LVGL TOUCH DEBUG");
  lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
  lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
  lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 12);

  gRawLabel = lv_label_create(screen);
  lv_label_set_text(gRawLabel, "RAW released\nTouch not seen yet");
  lv_obj_set_style_text_font(gRawLabel, &lv_font_montserrat_18, 0);
  lv_obj_set_style_text_color(gRawLabel, lv_color_hex(0x9EE493), 0);
  lv_obj_align(gRawLabel, LV_ALIGN_TOP_LEFT, 16, 56);

  gEventLabel = lv_label_create(screen);
  lv_label_set_text(gEventLabel, "LVGL button clicked: 0");
  lv_obj_set_style_text_font(gEventLabel, &lv_font_montserrat_18, 0);
  lv_obj_set_style_text_color(gEventLabel, lv_color_hex(0xFFD166), 0);
  lv_obj_align(gEventLabel, LV_ALIGN_TOP_LEFT, 16, 118);

  lv_obj_t *btn = lv_btn_create(screen);
  lv_obj_set_size(btn, 360, 110);
  lv_obj_align(btn, LV_ALIGN_CENTER, 0, 36);
  lv_obj_set_style_radius(btn, 18, 0);
  lv_obj_set_style_bg_color(btn, lv_color_hex(0x2A9D8F), 0);
  lv_obj_set_style_border_width(btn, 3, 0);
  lv_obj_set_style_border_color(btn, lv_color_hex(0xBDE0FE), 0);
  lv_obj_add_event_cb(btn, bigButtonEventCb, LV_EVENT_CLICKED, nullptr);

  lv_obj_t *btnLabel = lv_label_create(btn);
  lv_label_set_text(btnLabel, "Tap Here");
  lv_obj_set_style_text_font(btnLabel, &lv_font_montserrat_24, 0);
  lv_obj_set_style_text_color(btnLabel, lv_color_hex(0xFFFFFF), 0);
  lv_obj_center(btnLabel);

  lv_obj_t *hint = lv_label_create(screen);
  lv_label_set_text(hint, "Need both: RAW coords change and LVGL button click.");
  lv_obj_set_width(hint, 430);
  lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
  lv_obj_set_style_text_font(hint, &lv_font_montserrat_16, 0);
  lv_obj_set_style_text_color(hint, lv_color_hex(0xD9D9D9), 0);
  lv_obj_align(hint, LV_ALIGN_BOTTOM_MID, 0, -34);

  gMarker = lv_obj_create(screen);
  lv_obj_set_size(gMarker, 16, 16);
  lv_obj_set_style_radius(gMarker, LV_RADIUS_CIRCLE, 0);
  lv_obj_set_style_bg_color(gMarker, lv_color_hex(0xFF4D6D), 0);
  lv_obj_set_style_border_width(gMarker, 2, 0);
  lv_obj_set_style_border_color(gMarker, lv_color_hex(0xFFFFFF), 0);
  lv_obj_add_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
}

void setup() {
  Serial.begin(115200);
  Wire.begin(IIC_SDA, IIC_SCL);

  pinMode(TP_RST, OUTPUT);
  pinMode(TP_INT, INPUT);
  digitalWrite(TP_RST, LOW);
  delay(30);
  digitalWrite(TP_RST, HIGH);
  delay(50);
  delay(1000);

  gTouch.setPins(TP_RST, TP_INT);
  bool touchOk = gTouch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
  if (!touchOk) {
    Serial.println("Touch init failed");
  } else {
    Serial.print("Touch model: ");
    Serial.println(gTouch.getModelName());
    gTouch.sleep();
    gTouch.reset();
    gTouch.setMaxCoordinates(480, 480);
    gTouch.setSwapXY(true);
    gTouch.setMirrorXY(true, false);
  }

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

  lv_init();

  uint32_t screenWidth = gfx->width();
  uint32_t screenHeight = gfx->height();
  gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
  gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
  lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);

  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 lvglTickTimerArgs = {
      .callback = &lvglTick,
      .name = "lvgl_tick"};
  esp_timer_handle_t lvglTickTimer = nullptr;
  esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
  esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);

  createUi();
  Serial.println("LVGL touch debug test ready");
}

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