#define BOARD_ID adafruit_qtpy_esp32s2
// THIS WONT BUILD WITHOUT PRIVATE SERVER, EVEN WITH ASSETS REMOVED.
// It's "just" under 1megabyte in code + assets 
#define WOKWI 1
//Currently setup to use lilygo TDisplayS3 (320x170) which uses intel8080 parallel interface to ST7789 driver.
//Will be re-written to use ILI9341 (320x240 screen) and FT6206 touch IC

// Set filesystem to >2MB app, currently designed for T-Display-S3 16MB flash 8mb PSRAM (OPI)

#define DEBUG 0   // Set to 1 to wait for serial before starting routine... 
                  // NB: send enter in serial monitor to begin

#include "Arduino.h"
#include "OneButton.h"
#include "WiFi.h"
#include "cstxx.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "factory_gui.h"
#include "lvgl.h"
#include "pin_config.h"
#include "sntp.h"
#include "time.h"
#include "sdkconfig.h"
#include "ui.h"

#define LV_DELAY(x)                                                                                                                                  \
  do {                                                                                                                                               \
    uint32_t t = x;                                                                                                                                  \
    while (t--) {                                                                                                                                    \
      lv_timer_handler();                                                                                                                            \
      button1.tick();                                                                                                                                \
      button2.tick();                                                                                                                                \
      delay(1);                                                                                                                                      \
    }                                                                                                                                                \
  } while (0);



void ui_init(void);

esp_lcd_panel_io_handle_t io_handle = NULL;
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv;      // contains callback functions
static lv_color_t *lv_disp_buf;
static bool is_initialized_lvgl = false;
OneButton button1(PIN_BUTTON_1, true);
OneButton button2(PIN_BUTTON_2, true);
CSTXXX touch(0, 0, 170, 320);

bool SKIP_CURRENT = 0;

bool inited_touch = false;

struct wifi {
  String ssid;
  String password;
  byte rssi;
} cred1,cred2,cred3,cred4;
//Good-enough for now : Look at wifimanager library, or me-no-dev/ASyncWebServer etc

struct wifi Wifis[4];
int topRSSI=-1000;
wifi chosenWifi;

extern String WifiTemplateStringSTA = 
"Wifi Connected\nConnected AP:\nssid: {ssid}\nquality: -45db\nip: {ip}\n\nHelpful QRCode to\njoin wifi then visit\nhttp://{ip}\nin web browser";

extern String WifiTemplateStringAP = 
"Wifi Connected\nBroadcasting AP:\nssid: {ssid}\npw: {pass}\nip: {ip}\n\nHelpful QRCode to\njoin wifi then visit\nhttp://{ip}\nin web browser";


extern const unsigned char img_logo[];

void wifi_test(void);
void timeavailable(struct timeval *t);
void printLocalTime();
void SmartConfig();

static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) {
  if (is_initialized_lvgl) {
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    lv_disp_flush_ready(disp_driver);
  }
  return false;
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) {
  esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
  int offsetx1 = area->x1;
  int offsetx2 = area->x2;
  int offsety1 = area->y1;
  int offsety2 = area->y2;
  // copy a buffer's content to a specific area of the display
  esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

void setup() {
  cred1.ssid="free4all_2G";
  cred1.password="password";
  cred2.ssid="Tenda_60C06C";
  cred2.password="south5358";
  cred3.ssid="NETGEAR94";
  cred3.password="classytuba580";
  cred4.ssid="SENSOR-NET";
  cred4.password="password";
  Wifis[0]=cred1;
  Wifis[1]=cred2;
  Wifis[2]=cred3;
  Wifis[3]=cred4;
  chosenWifi.ssid = WIFI_SSID;
  chosenWifi.password = WIFI_PASSWORLD; // Default misspelt example WIFI_PASSWORD from factory.ino/pin_config.h  
  pinMode(PIN_POWER_ON, OUTPUT);
  digitalWrite(PIN_POWER_ON, HIGH);
  Serial.begin(115200);
#if(DEBUG==1)
  while(!Serial.available())
    LV_DELAY(10);
#endif
  
  Serial.println("Default wifi:");
  Serial.println(WIFI_SSID);
  sntp_servermode_dhcp(1); // (optional)
  //configTime(GMT_OFFSET_SEC, DAY_LIGHT_OFFSET_SEC, NTP_SERVER1, NTP_SERVER2);
  configTzTime(TIMEZONE, NTP_SERVER1, NTP_SERVER2);

  #pragma region I8080 setup

  pinMode(PIN_LCD_RD, OUTPUT);
  digitalWrite(PIN_LCD_RD, HIGH);
  esp_lcd_i80_bus_handle_t i80_bus = NULL;
  esp_lcd_i80_bus_config_t bus_config = {
      .dc_gpio_num = PIN_LCD_DC,
      .wr_gpio_num = PIN_LCD_WR,
      .clk_src = LCD_CLK_SRC_PLL160M,
      .data_gpio_nums =
          {
              PIN_LCD_D0,
              PIN_LCD_D1,
              PIN_LCD_D2,
              PIN_LCD_D3,
              PIN_LCD_D4,
              PIN_LCD_D5,
              PIN_LCD_D6,
              PIN_LCD_D7,
          },
      .bus_width = 8,
      .max_transfer_bytes = LVGL_LCD_BUF_SIZE * sizeof(uint16_t),
  };
  esp_lcd_new_i80_bus(&bus_config, &i80_bus);

  esp_lcd_panel_io_i80_config_t io_config = {
      .cs_gpio_num = PIN_LCD_CS,
      .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
      .trans_queue_depth = 20,
      .on_color_trans_done = example_notify_lvgl_flush_ready,
      .user_ctx = &disp_drv,
      .lcd_cmd_bits = 8,
      .lcd_param_bits = 8,
      .dc_levels =
          {
              .dc_idle_level = 0,
              .dc_cmd_level = 0,
              .dc_dummy_level = 0,
              .dc_data_level = 1,
          },
  };
  ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));
  esp_lcd_panel_handle_t panel_handle = NULL;
  esp_lcd_panel_dev_config_t panel_config = {
      .reset_gpio_num = PIN_LCD_RES,
      .color_space = ESP_LCD_COLOR_SPACE_RGB,
      .bits_per_pixel = 16,
  };
  esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle);
  esp_lcd_panel_reset(panel_handle);
  esp_lcd_panel_init(panel_handle);
  esp_lcd_panel_invert_color(panel_handle, true);

  esp_lcd_panel_swap_xy(panel_handle, true);
  esp_lcd_panel_mirror(panel_handle, false, true);
  // the gap is LCD panel specific, even panels with the same driver IC, can
  // have different gap value
  esp_lcd_panel_set_gap(panel_handle, 0, 35);
  
  #pragma endregion I8080 setup



  #pragma region lvgl init

  lv_init();
  lv_disp_buf = (lv_color_t *)heap_caps_malloc(LVGL_LCD_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);

  lv_disp_draw_buf_init(&disp_buf, lv_disp_buf, NULL, LVGL_LCD_BUF_SIZE);
  /*Initialize the display*/
  lv_disp_drv_init(&disp_drv);
  /*Change the following line to your display resolution*/
  disp_drv.hor_res = EXAMPLE_LCD_H_RES;
  disp_drv.ver_res = EXAMPLE_LCD_V_RES;
  disp_drv.flush_cb = example_lvgl_flush_cb;
  disp_drv.draw_buf = &disp_buf;
  disp_drv.user_data = panel_handle;
  lv_disp_drv_register(&disp_drv);

  is_initialized_lvgl = true;
  #pragma endregion lvgl init


  Serial.println("Lcd init");
  /* Lighten the screen with gradient */

  ledcSetup(0, 10000, 8);
  ledcAttachPin(PIN_LCD_BL, 0);
  for (uint8_t i = 0; i < 0xFF; i++) {
    ledcWrite(0, i);
    LV_DELAY(10);
  }


  Wire.begin(PIN_IIC_SDA, PIN_IIC_SCL);
  inited_touch = touch.init(Wire, PIN_TOUCH_RES, PIN_TOUCH_INT);

  button1.attachDoubleClick([]() {
    SKIP_CURRENT = true;
    Serial.println("Specifying skip");
  });

  wifi_test();
  button1.reset();
  button1.attachClick([]() {
    pinMode(PIN_POWER_ON, OUTPUT);
    pinMode(PIN_LCD_BL, OUTPUT);
    digitalWrite(PIN_POWER_ON, LOW);
    digitalWrite(PIN_LCD_BL, LOW);
    // Reset wifi to avoid reconnect issues
    WiFi.disconnect();
    WiFi.mode(WIFI_OFF);
    esp_sleep_enable_ext0_wakeup((gpio_num_t)PIN_BUTTON_2, 0); // 1 = High, 0 = Low
    esp_deep_sleep_start();
  });

  button2.attachClick([]() { ui_switch_page(); });
  button2.attachLongPressStop([](){
    button1.reset();
    button2.reset();
    button1.attachClick([](){
      Serial.println("btn1");
      ESP.restart();
    });
    button2.attachClick([](){
      Serial.println("btn2");
      ESP.restart();
    });
    ui_init();
  });
}

bool isKnown(String ssid, int rssi){
  int n = sizeof(Wifis) / sizeof(*Wifis);
  for (int i = 0; i < n; ++i) {
    if(Wifis[i].ssid == ssid){
      Serial.println("found known network");
      if(rssi >= topRSSI){
        topRSSI = rssi;
        chosenWifi = Wifis[i];
      }
      return true;
    }
  }
  return false;
}

void loop() {
  // lv_timer_handler();
  // button1.tick();
  // button2.tick();
  LV_DELAY(1);
  static uint32_t last_tick;
  if (millis() - last_tick > 100) {
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
      lv_msg_send(MSG_NEW_HOUR, &timeinfo.tm_hour);
      lv_msg_send(MSG_NEW_MIN, &timeinfo.tm_min);
    }
    uint32_t volt = (analogRead(PIN_BAT_VOLT) * 2 * 3.3 * 1000) / 4096;
    lv_msg_send(MSG_NEW_VOLT, &volt);

    if (inited_touch) {
      touch_info_t t;
      String str_buf;
      static uint8_t last_finger;
      touch.get_touch_point(&t);
      str_buf += " Finger num :";
      str_buf += t.finger_num;
      str_buf += " \n";
      for (uint8_t i = 0; i < t.finger_num; i++) {
        str_buf += "x:";
        str_buf += t.point[i].x;
        str_buf += " y:";
        str_buf += t.point[i].y;
        str_buf += " p:";
        str_buf += t.point[i].pressure;
        str_buf += " \n";
      }
      lv_msg_send(MSG_NEW_TOUCH_POINT, str_buf.c_str());
    }
    last_tick = millis();
  }
}

LV_IMG_DECLARE(good_enough_final);

void wifi_test(void) {
  String text;
  lv_obj_t *logo_img = lv_gif_create(lv_scr_act());
  lv_obj_center(logo_img);
  lv_gif_set_src(logo_img, &good_enough_final);
  int delayCount = 0;
  SKIP_CURRENT=false;
  while(delayCount<480 && !SKIP_CURRENT){
    LV_DELAY(10);
    delayCount++;
  }
  SKIP_CURRENT=false;
  lv_obj_del(logo_img);

  ui_init();
  LV_DELAY(1000)

//  
//  lv_obj_t *log_label = lv_label_create(lv_scr_act());
//  lv_obj_align(log_label, LV_ALIGN_TOP_LEFT, 0, 0);
//  lv_obj_set_width(log_label, LV_PCT(100));

  lv_textarea_set_text(ui_Screen2_TextArea1, "Scanning WiFi");
  // lv_label_set_long_mode(ui_Screen2_TextArea1, LV_LABEL_LONG_SCROLL);
  lv_label_set_recolor(ui_Screen2_TextArea1, true);
  LV_DELAY(1);
  // Reset wifi to avoid reconnect issues
  WiFi.disconnect();
  WiFi.mode(WIFI_OFF);
  LV_DELAY(100);
  WiFi.mode(WIFI_STA);
  LV_DELAY(100);
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0) {
    text = "no networks found";
  } else {
    text = n;
    text += " networks found\n";
    for (int i = 0; i < n; ++i) {
      text += (i + 1);
      text += ": ";
      text += WiFi.SSID(i);
      text += " (";
      text += WiFi.RSSI(i);
      text += ")";
      text += (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " OPEN" : " PWD";
      if(isKnown(WiFi.SSID(i), WiFi.RSSI(i))){
          text += " (Known)";
      }
      text += "\n";
      delay(10);
    }
  }
  LV_DELAY(250)
  lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
  Serial.println(text);
  LV_DELAY(500);
  text += "Connecting to ";
  Serial.print("Connecting to ");
  text += chosenWifi.ssid;
  text += "\n";
  Serial.print(chosenWifi.ssid);
  WiFi.begin(chosenWifi.ssid.c_str(), chosenWifi.password.c_str());
  uint32_t last_tick = millis();
  uint32_t i = 0;
  bool is_smartconfig_connect = false;
//  lv_label_set_long_mode(log_label, LV_LABEL_LONG_WRAP);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    text += ".";
    lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
    LV_DELAY(10);
    if (millis() - last_tick > WIFI_CONNECT_WAIT_MAX) { /* Automatically start smartconfig when connection times out */
      text += "\nConnection timed out, start smartconfig";
      lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
      LV_DELAY(100);
      is_smartconfig_connect = true;
      WiFi.mode(WIFI_AP_STA);
      Serial.println("\r\n wait for smartconfig....");
      text += "\r\n wait for smartconfig....";
      text += "\nPlease use EspTouch Apps to connect to the distribution network";
      lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
      WiFi.beginSmartConfig();
      while (1) {
        LV_DELAY(100);
        if (WiFi.smartConfigDone()) {
          Serial.println("\r\nSmartConfig Success\r\n");
          Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
          Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
          text += "\nSmartConfig Success";
          text += "\nSSID:";
          text += WiFi.SSID().c_str();
          text += "\nPSW:";
          text += WiFi.psk().c_str();
          lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
          LV_DELAY(1000);
          last_tick = millis();
          break;
        }
      }
    }
  }
  if (!is_smartconfig_connect) {
    text += "\nCONNECTED \nTakes ";
    Serial.print("\n CONNECTED \nTakes ");
    text += millis() - last_tick;
    Serial.print(millis() - last_tick);
    text += " ms\n";
    Serial.println(" millseconds");
    lv_textarea_set_text(ui_Screen2_TextArea1, text.c_str());
  }
  LV_DELAY(2000);
  ui_begin();
}

void printLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("No time available (yet)");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}




///////////////////////////////////////////////////
///////////////////////////////////////////////////
///////////////////////////////////////////////////

// #include <Wippersnapper.h>
// #include <WiFi.h>
// #include <HTTPClient.h>
// #include <ArduinoJson.h>
// #include <Adafruit_GFX.h>
// #include <Adafruit_ILI9341.h>

const char* ssid = "Wokwi-GUEST";
const char* password = "";

// #define BTN_PIN 5
// #define TFT_DC 2
// #define TFT_CS 15
// Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);


// const String url = "https://v2.jokeapi.dev/joke/Programming";

// String getJoke() {
//   HTTPClient http;
//   http.useHTTP10(true);
//   http.begin(url);
//   http.GET();
//   String result = http.getString();

//   DynamicJsonDocument doc(2048);
//   DeserializationError error = deserializeJson(doc, result);

//   // Test if parsing succeeds.
//   if (error) {
//     Serial.print("deserializeJson() failed: ");
//     Serial.println(error.c_str());
//     return "<error>";
//   }

//   String type = doc["type"].as<String>();
//   String joke = doc["joke"].as<String>();
//   String setup = doc["setup"].as<String>();
//   String delivery = doc["delivery"].as<String>();
//   http.end();
//   return type.equals("single") ? joke : setup + "  " + delivery;
// }

// void nextJoke() {
//   tft.setTextColor(ILI9341_WHITE);
//   tft.println("\nLoading joke...");

//   String joke = getJoke();
//   tft.setTextColor(ILI9341_GREEN);
//   tft.println(joke);
// }

// void setup() {
//   pinMode(BTN_PIN, INPUT_PULLUP);

//   WiFi.begin(ssid, password, 6);

//   tft.begin();
//   tft.setRotation(1);

//   tft.setTextColor(ILI9341_WHITE);
//   tft.setTextSize(2);
//   tft.print("Connecting to WiFi");

//   while (WiFi.status() != WL_CONNECTED) {
//     delay(100);
//     tft.print(".");
//   }

//   tft.print("\nOK! IP=");
//   tft.println(WiFi.localIP());

//   nextJoke();
// }

// void loop() {
//   if (digitalRead(BTN_PIN) == LOW) {
//     tft.fillScreen(ILI9341_BLACK);
//     tft.setCursor(0, 0);
//     nextJoke();
//   }

//   delay(100);
// }