#include <Arduino.h>
#include "ui.h"
#include "screens.h"
#include "images.h"
#include "actions.h"
#include "vars.h"
#include <string.h>
#include <Arduino_GFX_Library.h>
#include <TouchDrvCSTXXX.hpp>
#include <Ticker.h>

Arduino_DataBus *bus = new Arduino_ESP32SPI(3 /* DC */, 5 /* CS */, 2 /* SCK */, 4 /* MOSI */, -1 /* MISO */, FSPI /* spi_num */);
Arduino_GC9A01 *gfx = new Arduino_GC9A01(bus, 0 /* RST */, 2 /* rotation */, true /* IPS */);
TouchDrvCSTXXX touch;
Ticker fadeTicker;
bool  isPressed = false,sleeping=false;
unsigned long lastTouch;
static int16_t currentScreen = -1;
static enum ChartsEnum currentChart = CHART_ID_TEMP;

static lv_obj_t *getLvglObjectFromIndex(int32_t index) {
    if (index == -1) {
        return 0;
    }
    return ((lv_obj_t **)&objects)[index];
}

void loadScreen(ScreensEnum screenId) {
    currentScreen = screenId - 1;
    lv_obj_t *screen = getLvglObjectFromIndex(currentScreen);
    lv_scr_load_anim(screen, LV_SCR_LOAD_ANIM_FADE_IN, 200, 0, false);
}

void fadeBrightness(uint16_t _newBrightness) {
    uint16_t bright = ledcRead(GFX_BL);
    if (_newBrightness > bright) {
        bright += 10;
    } else if (_newBrightness < bright) {
        bright -= 10;
    }
    ledcWrite(GFX_BL, bright);
    if (_newBrightness == bright) {
        fadeTicker.detach();
    }
}

void setBacklight(uint16_t brightness, uint16_t fadeTime){
    if(brightness > BL_BRIGHTNESS)
        brightness = BL_BRIGHTNESS;
    ledcWrite(GFX_BL,brightness);
   /* uint16_t fadeTickTime = fadeTime/(abs(brightness - (uint16_t)ledcRead(GFX_BL))/10);
    if(fadeTickTime < 1)
        fadeTickTime = 1;
    fadeTicker.detach();
    fadeTicker.attach_ms(fadeTickTime, fadeBrightness,brightness);*/
}


void DisplayInit(void){
    ledcAttachPin(GFX_BL, GFX_BL);
    ledcSetup(GFX_BL, 2000, 10);
    ledcWrite(GFX_BL, 0);
    gfx->begin(80000000);
    gfx->fillScreen(0x0000);
}




void loadChart(uint8_t chartId){
    if(chartId < 1 || chartId >= CHART_ID_LEN)
        return;
    switch(chartId){
        case CHART_ID_TEMP:
        lv_label_set_text(objects.chart_header,"Temperature");
        lv_chart_set_ext_y_array(objects.chart,objects.chart_series,history_getTempPt());
        break;
        case CHART_ID_HUM:
        lv_label_set_text(objects.chart_header,"Humidity");
        lv_chart_set_ext_y_array(objects.chart,objects.chart_series,history_getHumPt());
        break;
        case CHART_ID_PRESS:
        lv_label_set_text(objects.chart_header,"Pressure");
        lv_chart_set_ext_y_array(objects.chart,objects.chart_series,history_getPressPt());
        break;
    }
    currentChart = (enum ChartsEnum) chartId;
    chart_autoscale();
}

ScreensEnum ui_getCurrentScreen(void){
    return (ScreensEnum) (currentScreen + 1);
}

ChartsEnum ui_getCurrentChart(void){
    return (ChartsEnum) currentChart;
}

void ui_init() {
    create_screens();
    loadScreen(SCREEN_ID_MAIN);
}

bool ui_isSleeping(void){
    return sleeping;
}

void ui_exitSleep(){
    if(sleeping){
        isPressed = true;
    }else{
        lastTouch = millis();
    }
}

void ui_tick() {
    if(millis() - lastTouch  > SLEEP_TIMEOUT){
        if(!sleeping){
            displaySleep();
            sleeping = true;
        }
        unsigned long millisbefore = millis();
        //while((millis()-millisbefore) < 10000 && (millis() - lastTouch)  > SLEEP_TIMEOUT){
            delay(200);
            if (isPressed){
                lastTouch = millis();
            }else{
                lastTouch = millis()-SLEEP_TIMEOUT-1;
            }
        //}
    }else if(sleeping){
        Serial.print("WAKE!!");
        lastTouch = millis();
        displayWake();
        lv_obj_invalidate(objects.temp_arc);
        lv_task_handler();  // let the GUI do its work
        sleeping= false;
        isPressed = false;
    }
}

void displaySleep(){
    gfx->displayOff();
    setBacklight(0,300);
}
  
void displayWake(){
    //Serial.println("wakeup");
    gfx->displayOn();
    delay(10);
    lv_obj_invalidate(objects.temp_arc);
    lv_task_handler();  // let the GUI do its work
    setBacklight(BL_BRIGHTNESS,200);
}
  
  
  void TouchInit()
  {
    pinMode(TouchRST, OUTPUT);
    digitalWrite(TouchRST, LOW);
    delay(30);
    digitalWrite(TouchRST, HIGH);
    delay(50);
    
    touch.setPins(TouchRST, TouchInt);
    Wire.begin(TouchSDA,TouchSCL);   //初始化IIC，设置引脚 SCL:22  SDA:21
    Wire.setClock(200000);  //设置频率400KHZ
    if(touch.begin(Wire, TouchI2CAddr, SDA, SCL)){
      Serial.println("Touch screen initialization done");
    }
    attachInterrupt(TouchInt, []() {
      isPressed = true;
    }, CHANGE);
  }
  
  
  
  void my_touch_read(lv_indev_t * indev, lv_indev_data_t * data)
  {
    int16_t x = 0;
    int16_t y = 0;
    if (isPressed && !sleeping) {
        lastTouch = millis();
        isPressed = false;
        uint8_t touched = touch.getPoint(&x, &y, 1);
        if (touched) {
          data->point.x = SCREEN_WIDTH - x;
          data->point.y = SCREEN_HEIGHT - y;
          data->state = LV_INDEV_STATE_PRESSED;
        }else {
            data->state = LV_INDEV_STATE_RELEASED;
        }
    }
    
  }
  
void log_print(lv_log_level_t level, const char * buf) {
    LV_UNUSED(level);
    Serial.println(buf);
    Serial.flush();
}
  
  
  void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map)
  {
      /* The most simple case (also the slowest) to send all rendered pixels to the
       * screen one-by-one.  `put_px` is just an example.  It needs to be implemented by you. */
      uint16_t * buf16 = (uint16_t *)px_map; /* Let's say it's a 16 bit (RGB565) display */
      int32_t x, y;
      gfx->startWrite();
      gfx->writeAddrWindow(area->x1, area->y1, area->x2 - area->x1+1, area->y2 - area->y1+1);
      //bus->writeBytes(px_map,(uint32_t) (1+area->y2 - area->y1) * (1+area->x2 - area->x1));
      for(y = area->y1; y <= area->y2; y++) {
          for(x = area->x1; x <= area->x2; x++) {
      //        //gfx->writePixel(x, y, *buf16);
              bus->write16(*buf16);
      //        bus->write16bitBeRGBBitmapR1()
              buf16++;
          }
      }
      gfx->endWrite();
      /* IMPORTANT!!!
       * Inform LVGL that flushing is complete so buffer can be modified again. */
      lv_display_flush_ready(display);
  }
