// ESP32-S3 + JC3248W535EN (AXS15231B via QSPI) + Arduino_Canvas + LVGL 8.3.11 + SquareLine
// Libraries: GFX Library for Arduino v1.6.0, lvgl 8.3.x

#include <Arduino.h>
#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <lvgl.h>

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// === WiFi & Remote Device ===
#ifndef WIFI_SSID
#define WIFI_SSID "DIN_WIFI_SSID"
#endif
#ifndef WIFI_PASSWORD
#define WIFI_PASSWORD "DIT_WIFI_PASSWORD"
#endif

#ifndef DEVICE_HOST
// Sæt IP/host til den anden ESP32 der styrer LED og giver temperatur-API
#define DEVICE_HOST "IP her" // Din IP fra din ESP32 som du skal modtage DATA fra skal sætte ind her
#endif

// ----- SquareLine: brug forward declarations i stedet for #include "ui.h" -----
extern "C" {
  void ui_init(void);
  extern lv_obj_t * ui_uiButtonLED;
  extern lv_obj_t * ui_uiLabelTemp;
  extern lv_obj_t * ui_uiLedIndicator;
}

// Tilstand
static bool g_ledOn = false;
static lv_timer_t* g_tempTimer = nullptr;

/*** Pins ***/
#define PIN_BL      1
#define TOUCH_SDA   4
#define TOUCH_SCL   8
#define TOUCH_RST   12
#define TOUCH_INT   11   // valgfri; vi poller uanset

/*** Display (AXS15231B QSPI + Canvas) ***/
Arduino_DataBus *bus   = new Arduino_ESP32QSPI(45, 47, 21, 48, 40, 39);
Arduino_GFX     *panel = new Arduino_AXS15231B(bus, GFX_NOT_DEFINED, 0, false, 320, 480); // panel i rot=0
Arduino_Canvas  *gfx   = new Arduino_Canvas(320, 480, panel, 0, 0, 0);                    // canvas roteres i setup()

/*** LVGL ***/
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf1 = nullptr, *buf2 = nullptr;

/*** Touch – “parser A” (som i din test) ***/
static const uint8_t AXS_READ_TOUCHPAD[8] = { 0xB5, 0xAB, 0xA5, 0x5A, 0x00, 0x00, 0x00, 0x08 };
static bool read_axs_touch(uint16_t &tx, uint16_t &ty)
{
  Wire.beginTransmission(0x3B);
  Wire.write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD));
  if (Wire.endTransmission(false) != 0) return false;

  uint8_t d[8];
  if (Wire.requestFrom(0x3B, 8) < 8) return false;
  for (uint8_t i = 0; i < 8; i++) d[i] = Wire.read();

  if (d[0] != 0 || d[1] == 0) return false;

  uint16_t x = ((uint16_t)(d[2] & 0x0F) << 8) | d[3];
  uint16_t y = ((uint16_t)(d[4] & 0x0F) << 8) | d[5];

  // Landscape (ROTATION=1) – samme mapping som din test:
  tx = y;
  ty = 320 - 1 - x;
  return true;
}

/*** LVGL flush: LVGL -> Canvas -> flush() ***/
static void lvgl_flush_cb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  uint16_t w = (uint16_t)(area->x2 - area->x1 + 1);
  uint16_t h = (uint16_t)(area->y2 - area->y1 + 1);
  gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t*)color_p, w, h);
  gfx->flush();  // v1.6.0: fuld flush
  lv_disp_flush_ready(disp);
}

/*** LVGL touch-driver ***/
static void lv_touch_read_cb(lv_indev_drv_t *, lv_indev_data_t *data)
{
  uint16_t x, y;
  if (read_axs_touch(x, y)) {
    data->point.x = x;
    data->point.y = y;
    data->state   = LV_INDEV_STATE_PRESSED;
  } else {
    data->state   = LV_INDEV_STATE_RELEASED;
  }
}

// ---------- UI helper ----------
static void ui_update_led_indicator(bool on) {
  if (!ui_uiLedIndicator) return;
  lv_color_t color = on ? lv_palette_main(LV_PALETTE_GREEN) : lv_palette_main(LV_PALETTE_RED);
  lv_obj_set_style_bg_color(ui_uiLedIndicator, color, LV_PART_MAIN);
}

static void update_temp_label(float t, float h) {
  if (!ui_uiLabelTemp) return;
  char buf[64];
  if (isnan(t)) snprintf(buf, sizeof(buf), "Temp: --.- °C");
  else {
    if (!isnan(h)) snprintf(buf, sizeof(buf), "Temp: %.1f °C\nFugt: %.0f %%", t, h);
    else           snprintf(buf, sizeof(buf), "Temp: %.1f °C", t);
  }
  lv_label_set_text(ui_uiLabelTemp, buf);
}

// ---------- HTTP ----------
static bool http_led(bool on) {
  HTTPClient http;
  http.setConnectTimeout(2500);
  String url = String(DEVICE_HOST) + "/api/led?state=" + (on ? "on" : "off");
  if (!http.begin(url)) return false;
  int code = http.GET();
  Serial.printf("[Display] GET %s -> %d\n", url.c_str(), code);
  http.end();
  return code == 200;
}

static void poll_temp_cb(lv_timer_t* tmr) {
  (void)tmr;
  HTTPClient http;
  http.setConnectTimeout(2500);
  http.setTimeout(2500);
  String url = String(DEVICE_HOST) + "/api/temp";
  if (!http.begin(url)) return;
  int code = http.GET();
  if (code == 200) {
    // For debugging: vis RAW i Serial hvis du vil
    // String raw = http.getString(); Serial.println(raw);

    StaticJsonDocument<192> doc;
    DeserializationError err = deserializeJson(doc, http.getStream());
    if (!err) {
      float t = doc["temp_c"]  | NAN;
      float h = doc["hum_pct"] | NAN;
      update_temp_label(t, h);
    } else {
      Serial.printf("[Display] JSON error: %s\n", err.c_str());
    }
  } else {
    Serial.printf("[Display] GET /api/temp -> %d\n", code);
  }
  http.end();
}

// ---------- Knap-event ----------
static void btn_led_event_cb(lv_event_t * e) {
  if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
  bool newState = !g_ledOn;
  if (http_led(newState)) {
    g_ledOn = newState;
    ui_update_led_indicator(g_ledOn);
  } else {
    Serial.println("[Display] LED-kald fejlede");
  }
}

// ---------- Net init ----------
static void net_app_begin() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  unsigned long ms = millis();
  Serial.print("[Display] Forbinder til WiFi");
  while (WiFi.status() != WL_CONNECTED && (millis()-ms) < 15000) {
    delay(200); Serial.print(".");
  }
  Serial.println();
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("[Display] IP = "); Serial.println(WiFi.localIP());
  } else {
    Serial.println("[Display] WiFi timeout (fortsætter, men HTTP vil fejle)");
  }

  // bind knap-event
  if (ui_uiButtonLED) {
    lv_obj_add_event_cb(ui_uiButtonLED, btn_led_event_cb, LV_EVENT_CLICKED, NULL);
  }

  // start temp-poll (rigtig temperatur fra ekstern ESP32)
  if (!g_tempTimer) g_tempTimer = lv_timer_create(poll_temp_cb, 5000, NULL);
}

// (valgfrit) kald i loop hvis du senere vil overvåge WiFi
static void net_app_tick() {
  // intet nødvendigt pt.
}

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

  // Backlight
  pinMode(PIN_BL, OUTPUT);
  digitalWrite(PIN_BL, HIGH);

  // Touch I2C
  Wire.begin(TOUCH_SDA, TOUCH_SCL, 400000);  // 400 kHz
  if (TOUCH_RST >= 0) {
    pinMode(TOUCH_RST, OUTPUT);
    digitalWrite(TOUCH_RST, LOW);  delay(5);
    digitalWrite(TOUCH_RST, HIGH); delay(10);
  }
  if (TOUCH_INT >= 0) pinMode(TOUCH_INT, INPUT_PULLUP);

  // Init display via Canvas (KUN gfx->begin())
  if (!gfx->begin()) { Serial.println("gfx->begin FAIL"); while (true) delay(1000); }

  // Landscape (ROTATION=1)
  gfx->setRotation(1);
  gfx->fillScreen(BLACK);
  gfx->flush();

  // LVGL
  lv_init();
  uint16_t W = gfx->width();   // 480
  uint16_t H = gfx->height();  // 320

  uint32_t buf_px = (uint32_t)W * 40; // ~40 linjer
  buf1 = (lv_color_t*)heap_caps_malloc(buf_px * sizeof(lv_color_t), MALLOC_CAP_DMA);
  buf2 = (lv_color_t*)heap_caps_malloc(buf_px * sizeof(lv_color_t), MALLOC_CAP_DMA);
  lv_disp_draw_buf_init(&draw_buf, buf1, buf2, buf_px);

  static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res  = W;
  disp_drv.ver_res  = H;
  disp_drv.flush_cb = lvgl_flush_cb;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register(&disp_drv);

  static lv_indev_drv_t indev_drv; lv_indev_drv_init(&indev_drv);
  indev_drv.type    = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = lv_touch_read_cb;
  lv_indev_drv_register(&indev_drv);

  // SquareLine UI
  ui_init();

  // Sæt en initial tekst
  lv_label_set_text(ui_uiLabelTemp, "Temp: --.- °C");

  Serial.println("SquareLine UI kører (AXS+Canvas, ROTATION=1, touch=parser A @0x3B).");

  // WiFi + net
  net_app_begin();
}

void loop()
{
  net_app_tick();
  lv_timer_handler();
  delay(1);
}
