#include "display.h"

#define BUTTON_PIN 19

lv_indev_t *indev_keypad;

// Define screen dimensions
#ifdef FNK0102A_1P14_135x240_ST7789
static const uint16_t screenWidth = 135;
static const uint16_t screenHeight = 240;
#elif defined(FNK0102B_3P5_320x480_ST7796)
static const uint16_t screenWidth = 320;
static const uint16_t screenHeight = 480;
#else
#error "No supported Freenove display model macro defined."
#endif

static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 5];

TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight);
Button button(BUTTON_PIN);
Display display;

#if LV_USE_LOG != 0
void my_print(const char *buf) {
  Serial0.print(buf);
}
#endif

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);

  tft.startWrite();
  tft.setAddrWindow(area->x1, area->y1, w, h);
  tft.pushColors((uint16_t *)&color_p->full, w * h, true);
  tft.endWrite();

  lv_disp_flush_ready(disp);
}

void my_keypad_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
  static int last_key = 0;

  button.key_scan();
  int buttonState = button.get_button_state();
  int act_key = button.get_button_key_value();

  switch (buttonState) {
    case Button::KEY_STATE_PRESSED:
      data->state = LV_INDEV_STATE_PR;
      break;
    case Button::KEY_STATE_RELEASED:
      data->state = LV_INDEV_STATE_REL;
      break;
    default:
      data->state = LV_INDEV_STATE_REL;
      break;
  }

  switch (act_key) {
    case 1:
      act_key = LV_KEY_ENTER;
      break;
    case 2:
      if (display.getTftShowDirection() == 0) act_key = LV_KEY_PREV;
      else if (display.getTftShowDirection() == 1) act_key = LV_KEY_LEFT;
      else if (display.getTftShowDirection() == 2) act_key = LV_KEY_NEXT;
      else if (display.getTftShowDirection() == 3) act_key = LV_KEY_RIGHT;
      break;
    case 3:
      if (display.getTftShowDirection() == 0) act_key = LV_KEY_NEXT;
      else if (display.getTftShowDirection() == 1) act_key = LV_KEY_RIGHT;
      else if (display.getTftShowDirection() == 2) act_key = LV_KEY_PREV;
      else if (display.getTftShowDirection() == 3) act_key = LV_KEY_LEFT;
      break;
    case 4:
      if (display.getTftShowDirection() == 0) act_key = LV_KEY_LEFT;
      else if (display.getTftShowDirection() == 1) act_key = LV_KEY_NEXT;
      else if (display.getTftShowDirection() == 2) act_key = LV_KEY_RIGHT;
      else if (display.getTftShowDirection() == 3) act_key = LV_KEY_PREV;
      break;
    case 5:
      if (display.getTftShowDirection() == 0) act_key = LV_KEY_RIGHT;
      else if (display.getTftShowDirection() == 1) act_key = LV_KEY_PREV;
      else if (display.getTftShowDirection() == 2) act_key = LV_KEY_LEFT;
      else if (display.getTftShowDirection() == 3) act_key = LV_KEY_NEXT;
      break;
    default:
      break;
  }

  last_key = act_key;
  data->key = last_key;
}

void tftRst(void) {
  Serial0.println("DISPLAY DBG - tftRst begin");
  // Backlight/reset toggling intentionally skipped for isolation
  delay(50);
  Serial0.println("DISPLAY DBG - tftRst complete");
}

void setupTFT(int direction) {
  Serial0.println("DISPLAY DBG - setupTFT begin");
  tftRst();

  display.setTftShowDirection(direction);

  Serial0.println("DISPLAY DBG - before tft.begin");
  tft.begin();
  Serial0.println("DISPLAY DBG - after tft.begin");

  tft.setRotation(display.getTftShowDirection());
  Serial0.println("DISPLAY DBG - after setRotation");

  tft.fillScreen(TFT_BLACK);
  Serial0.println("DISPLAY DBG - after fillScreen");
}

void setupButton() {
  Serial0.println("DISPLAY DBG - setupButton begin");
  button.init();
  Serial0.println("DISPLAY DBG - setupButton done");
}

void setupLVGL() {
  Serial0.println("DISPLAY DBG - setupLVGL begin");

#if LV_USE_LOG != 0
  lv_log_register_print_cb(my_print);
  Serial0.println("DISPLAY DBG - lv_log callback set");
#endif

  Serial0.println("DISPLAY DBG - before lv_init");
  lv_init();
  Serial0.println("DISPLAY DBG - after lv_init");

  Serial0.println("DISPLAY DBG - before lv_disp_draw_buf_init");
  lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * screenHeight / 5);
  Serial0.println("DISPLAY DBG - after lv_disp_draw_buf_init");

  static lv_disp_drv_t disp_drv;
  Serial0.println("DISPLAY DBG - before lv_disp_drv_init");
  lv_disp_drv_init(&disp_drv);
  Serial0.println("DISPLAY DBG - after lv_disp_drv_init");

  switch (display.getTftShowDirection()) {
    case 0:
    case 2:
      disp_drv.hor_res = screenWidth;
      disp_drv.ver_res = screenHeight;
      break;
    case 1:
    case 3:
      disp_drv.hor_res = screenHeight;
      disp_drv.ver_res = screenWidth;
      break;
    default:
      disp_drv.hor_res = screenWidth;
      disp_drv.ver_res = screenHeight;
      break;
  }

  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;

  Serial0.println("DISPLAY DBG - before lv_disp_drv_register");
  lv_disp_drv_register(&disp_drv);
  Serial0.println("DISPLAY DBG - after lv_disp_drv_register");

  static lv_indev_drv_t indev_drv;
  Serial0.println("DISPLAY DBG - before lv_indev_drv_init");
  lv_indev_drv_init(&indev_drv);
  Serial0.println("DISPLAY DBG - after lv_indev_drv_init");

  indev_drv.type = LV_INDEV_TYPE_KEYPAD;
  indev_drv.read_cb = my_keypad_read;

  Serial0.println("DISPLAY DBG - before lv_indev_drv_register");
  indev_keypad = lv_indev_drv_register(&indev_drv);
  Serial0.println("DISPLAY DBG - after lv_indev_drv_register");
}

void Display::init(int screenDir) {
  Serial0.println("DISPLAY DBG - Display::init begin");
  setupTFT(screenDir);
  setupButton();
  setupLVGL();
  Serial0.println("DISPLAY DBG - Display::init done");
}

void Display::routine(void) {
  lv_task_handler();
}