/**
 * @file lcd_display_drv.c
 * @author June6 (https://github.com/Junedoubling6)
 * @brief
 * @version 0.1
 * @date 2023-08-16
 *
 * @copyright Copyright (c) 2022 - , All rights reserved.
 *
 */
#include "lcd_display_drv.h"

#include "driver/gpio.h"
#include "driver/ledc.h"
#include "esp_system.h"
#include "log.h"
#include "rtos_drv.h"
#include "spi_drv.h"

#define LCD_DISPLAY_BL_ON_LEVEL 1
#define LCD_DISPLAY_BL_OFF_LEVEL !LCD_DISPLAY_BL_ON_LEVEL

#define LCD_DISPLAY_BK_LIGHT (GPIO_NUM_NC)
#define LCD_DISPLAY_BK_LIGHT_MODE LEDC_LOW_SPEED_MODE
#define LCD_DISPLAY_BK_LIGHT_TIMER LEDC_TIMER_0
#define LCD_DISPLAY_BK_LIGHT_CHANNEL LEDC_CHANNEL_0
/* 占空比分辨率设置为13位 */
#define LCD_DISPLAY_BK_LIGHT_DUTY_RES LEDC_TIMER_13_BIT
/* 频率设置为1.5 kHz */
#define LCD_DISPLAY_BK_LIGHT_FREQUENCY (1500)

static const char* TAG = "lcd_display_drv";
static bool lcd_display_work_state = false;
/* 默认占空比100% */
static uint8_t lcd_backlight_duty = 100;
/* 将数据放入DRAM; 默认情况下, 恒定数据会放入DROM, DMA无法访问该DROM */
DRAM_ATTR static const spi_init_cmd_t lcd_init_cmds[] = {
    {0xF0, {0x55, 0xAA, 0x52, 0x08, 0x00}, 5},
    {0xF6, {0x5A, 0x87}, 2},
    {0xC1, {0x3F}, 1},
    {0xCD, {0x25}, 1},
    {0xC9, {0x10}, 1},
    {0xF8, {0x8A}, 1},
    {0xAC, {0x45}, 1},
    {0xA7, {0x47}, 1},
    {0xA0, {0xCC}, 1},
    {0x86, {0x99, 0xA3, 0xA3, 0x41}, 4},
    {0xFA, {0x08, 0x08, 0x00, 0x04}, 4},
    {0xA3, {0x6E}, 1},
    {0xFD, {0x28, 0x3C, 0x00}, 3},
    {0x9A, {0x7C}, 1},
    {0x9B, {0x64}, 1},
    {0x82, {0x36, 0x36}, 2},
/* NOTE: June6 硬件处理180度旋转屏幕(X, Y轴均镜像) */
#if CONFIG_LCD_MIRROR_HARDWARE
/* NOTE: June6 反扫屏幕 */
/* NOTE: June6 硬件处理颜色反转 */
#if CONFIG_LCD_COLOR_INVERSION
    {0xB1, {0x93}, 1},
#else
    {0xB1, {0x13}, 1},
#endif
#else
/* NOTE: June6 硬件处理颜色反转 */
#if CONFIG_LCD_COLOR_INVERSION
    {0xB1, {0x90}, 1},
#else
    {0xB1, {0x10}, 1},
#endif
#endif
    {0x7A, {0x0F, 0x13}, 2},
    {0x7B, {0x0F, 0x13}, 2},
    {0x6D,
     {0x00, 0x1F, 0x19, 0x1A, 0x10, 0x0e, 0x0c, 0x0a, 0x02, 0x08, 0x1E,
      0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
      0x07, 0x01, 0x09, 0x0b, 0x0D, 0x0F, 0x1a, 0x19, 0x1f, 0x00},
     32},
    {0x64,
     {0x38, 0x04, 0x01, 0xdc, 0x03, 0x03, 0x38, 0x03, 0x01, 0xdd, 0x03, 0x03,
      0x0A, 0x78, 0x0A, 0x78},
     16},
    {0x65,
     {0x38, 0x02, 0x01, 0xde, 0x03, 0x03, 0x38, 0x01, 0x01, 0xdf, 0x03, 0x03,
      0x0A, 0x78, 0x0A, 0x78},
     16},
    {0x66,
     {0x38, 0x00, 0x01, 0xe0, 0x03, 0x03, 0x30, 0x01, 0x01, 0xe1, 0x03, 0x03,
      0x0A, 0x78, 0x0A, 0x78},
     16},
    {0x67,
     {0x30, 0x02, 0x01, 0xe2, 0x03, 0x03, 0x30, 0x03, 0x01, 0xe3, 0x03, 0x03,
      0x0A, 0x78, 0x0A, 0x78},
     16},
    {0x68,
     {0x3B, 0x08, 0x18, 0x08, 0x18, 0x5f, 0x7A, 0x08, 0x18, 0x08, 0x18, 0x5f,
      0x7A},
     13},
    {0x60, {0x38, 0x06, 0x0A, 0x78, 0x38, 0x05, 0x0A, 0x78}, 8},
    {0x63, {0x31, 0xde, 0x0A, 0x78, 0x31, 0xdf, 0x0A, 0x78}, 8},
    {0x69, {0x14, 0x22, 0x14, 0x22, 0x44, 0x22, 0x08}, 7},
    {0x6B, {0x07}, 1},
    {0xD1,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0xA8, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0xD2,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0xA8, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0xD3,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0xA8, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0xD4,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0x88, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0xD5,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0x88, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0xD6,
     {0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x50, 0x00, 0x6E, 0x00,
      0x8F, 0x00, 0xAf, 0x00, 0xDd, 0x01, 0x00, 0x01, 0x43, 0x01, 0x77,
      0x01, 0xcb, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x55, 0x02, 0x88, 0x02,
      0xC6, 0x03, 0x08, 0x03, 0x33, 0x03, 0x78, 0x03, 0x9E, 0x03, 0x9f,
      0x03, 0x9F, 0x03, 0x9F, 0x03, 0x9F, 0x03, 0xFF},
     52},
    {0x3A, {0x66}, 1},
    {0x11, {0x00}, 0},
    {0, {0x78}, 0},
    {0x29, {0x00}, 0},
    {0, {0}, 0xFF},
};
#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
DRAM_ATTR static const spi_init_cmd_t lcd_sleep_cmds[] = {
    {0xF0, {0x55, 0xAA, 0x52, 0x08, 0x00}, 5},
    {0xC1, {0x3F}, 1},
    {0x6C, {0x60}, 1},
    {0, {0x14}, 0},
    {0xB1, {0x00}, 1},
    {0xFA, {0x7F, 0x00, 0x00, 0x00}, 4},
    {0, {0x14}, 0},
    {0x6C, {0x50}, 1},
    {0, {0x0A}, 0},
    {0x28, {0x00}, 0},
    {0, {0x32}, 0},
    {0x10, {0x00}, 0},
    {0, {0x14}, 0},
    /* 下电代码 */
    {0xF0, {0x55, 0xAA, 0x52, 0x08, 0x00}, 5},
    {0xC2, {0xCE}, 1},
    {0xC3, {0xCD}, 1},
    {0xC6, {0xFC}, 1},
    {0xC5, {0x03}, 1},
    {0xCD, {0x64}, 1},
    {0xC4, {0xFF}, 1},
    {0xC9, {0xCD}, 1},
    {0xF6, {0x5A, 0x87}, 2},
    {0xFD, {0xAA, 0xAA, 0x0A}, 3},
    {0xFE, {0x6A, 0x0A}, 2},
    {0x78, {0x2A, 0xAA}, 2},
    {0x92, {0x17, 0x08}, 2},
    {0x77, {0xAA, 0x2A}, 2},
    {0x76, {0xAA, 0xAA}, 2},
    {0x84, {0x00}, 1},
    {0x78, {0x2B, 0xBA}, 2},
    {0x89, {0x73}, 1},
    {0x88, {0x3A}, 1},
    {0x85, {0xB0}, 1},
    {0x76, {0xEB, 0xAA}, 2},
    {0x94, {0x80}, 1},
    {0x87, {0x04, 0x07, 0x30}, 3},
    {0x93, {0x27}, 1},
    {0xAF, {0x02}, 1},
    {0, {0}, 0xFF},
};
#endif

/**
 * @brief LCD display gpio reset timing.
 *
 */
static void lcd_display_gpio_reset_timing(void) {
  ESP_ERROR_CHECK(gpio_set_level(LCD_DISPLAY_RST, 1));
  vTaskDelay(pdMS_TO_TICKS(5));
  ESP_ERROR_CHECK(gpio_set_level(LCD_DISPLAY_RST, 0));
  vTaskDelay(pdMS_TO_TICKS(5));
  ESP_ERROR_CHECK(gpio_set_level(LCD_DISPLAY_RST, 1));
  vTaskDelay(pdMS_TO_TICKS(30));
}

/**
 * @brief LCD display gpio sleep timing.
 *
 */
static void lcd_display_gpio_sleep_timing(void) {
  ESP_ERROR_CHECK(gpio_set_level(LCD_DISPLAY_RST, 1));
  vTaskDelay(pdMS_TO_TICKS(30));
}

/**
 * @brief Get the Lcd Display Work State object
 *
 * @return true
 * @return false
 */
bool get_lcd_display_work_state(void) { return lcd_display_work_state; }

/**
 * @brief Get the lcd backlight duty object
 *
 * @return uint8_t
 * @details 返回参数为百分比
 */
uint8_t get_lcd_backlight_duty(void) { return lcd_backlight_duty; }

/**
 * @brief Set the lcd backlight duty object
 *
 * @param duty
 * @return uint8_t
 * @details 返回参数为百分比
 */
uint8_t set_lcd_backlight_duty(uint8_t duty) {
  lcd_backlight_duty = duty;
  if (LCD_DISPLAY_BK_LIGHT == GPIO_NUM_NC) goto End;
  /* 例如: 占空比设置为100% (2 ^ 13) * 100% = 8192 */
  uint32_t duty_param = 8192 * lcd_backlight_duty / 100;
  if (duty_param > 0) {
    ESP_ERROR_CHECK(
        ledc_timer_rst(LCD_DISPLAY_BK_LIGHT_MODE, LCD_DISPLAY_BK_LIGHT_TIMER));
    ESP_ERROR_CHECK(ledc_timer_resume(LCD_DISPLAY_BK_LIGHT_MODE,
                                      LCD_DISPLAY_BK_LIGHT_TIMER));
    ESP_ERROR_CHECK(ledc_set_duty(LCD_DISPLAY_BK_LIGHT_MODE,
                                  LCD_DISPLAY_BK_LIGHT_CHANNEL, duty_param));
    /* 更新占空比的参数值 */
    ESP_ERROR_CHECK(ledc_update_duty(LCD_DISPLAY_BK_LIGHT_MODE,
                                     LCD_DISPLAY_BK_LIGHT_CHANNEL));
  } else {
    ESP_ERROR_CHECK(ledc_timer_pause(LCD_DISPLAY_BK_LIGHT_MODE,
                                     LCD_DISPLAY_BK_LIGHT_TIMER));
    ESP_ERROR_CHECK(
        ledc_stop(LCD_DISPLAY_BK_LIGHT_MODE, LCD_DISPLAY_BK_LIGHT_CHANNEL, 0));
  }

End:
  return lcd_backlight_duty;
}

/**
 * @brief Enable or disable lcd panel backlight.
 *
 * @param cmd
 */
void lcd_panel_backlight_enable(bool cmd) {
  /* TODO: June6 亮屏或灭屏 */
  if (LCD_DISPLAY_BK_LIGHT != GPIO_NUM_NC) {
    if (cmd) {
      set_lcd_backlight_duty(get_lcd_backlight_duty());
    } else {
      ESP_ERROR_CHECK(ledc_timer_pause(LCD_DISPLAY_BK_LIGHT_MODE,
                                       LCD_DISPLAY_BK_LIGHT_TIMER));
      ESP_ERROR_CHECK(ledc_stop(LCD_DISPLAY_BK_LIGHT_MODE,
                                LCD_DISPLAY_BK_LIGHT_CHANNEL, 0));
    }
  }
}

/**
 * @brief Config lcd display gpio.
 *
 * @details Turn off LCD backlight after config.
 */
void lcd_display_gpio_config(void) {
  if (LCD_DISPLAY_BK_LIGHT != GPIO_NUM_NC) {
    /* 准备并应用LEDC PWM定时器配置 */
    ledc_timer_config_t timer = {
        .speed_mode = LCD_DISPLAY_BK_LIGHT_MODE,
        .timer_num = LCD_DISPLAY_BK_LIGHT_TIMER,
        .duty_resolution = LCD_DISPLAY_BK_LIGHT_DUTY_RES,
        .freq_hz = LCD_DISPLAY_BK_LIGHT_FREQUENCY,
        .clk_cfg = LEDC_AUTO_CLK};
    ESP_ERROR_CHECK(ledc_timer_config(&timer));
    /* 准备并应用LEDC PWM通道配置 */
    ledc_channel_config_t channel = {.speed_mode = LCD_DISPLAY_BK_LIGHT_MODE,
                                     .channel = LCD_DISPLAY_BK_LIGHT_CHANNEL,
                                     .timer_sel = LCD_DISPLAY_BK_LIGHT_TIMER,
                                     .intr_type = LEDC_INTR_DISABLE,
                                     .gpio_num = LCD_DISPLAY_BK_LIGHT,
                                     .duty = 0,
                                     .hpoint = 0};
    ESP_ERROR_CHECK(ledc_channel_config(&channel));
    lcd_panel_backlight_enable(false);
  }

  if (LCD_DISPLAY_RST != GPIO_NUM_NC) {
    gpio_config_t rst_gpio_config = {.mode = GPIO_MODE_OUTPUT,
                                     .pin_bit_mask = 1ULL << LCD_DISPLAY_RST};
    /* 初始化rst的GPIO */
    ESP_ERROR_CHECK(gpio_config(&rst_gpio_config));
  }
}

/**
 * @brief Initialize lcd display rgb driver.
 *
 */
void lcd_display_rgb_driver_init(void) {
  lcd_display_gpio_reset_timing();
  ESP_ERROR_CHECK(spi_lcd_master_config(LCD_DISPLAY_SCLK, LCD_DISPLAY_MOSI,
                                        LCD_DISPLAY_MISO, LCD_DISPLAY_CS,
                                        LCD_DISPLAY_DC));
  uint8_t cmd_count = 0;
  /* TODO: June6 读取芯片ID */
  while (lcd_init_cmds[cmd_count].data_bytes != 0xFF) {
    if ((lcd_init_cmds[cmd_count].cmd != 0) ||
        (lcd_init_cmds[cmd_count].data_bytes != 0)) {
      spi_lcd_write(true, &(lcd_init_cmds[cmd_count].cmd), 1, false);
      spi_lcd_write(false, lcd_init_cmds[cmd_count].data,
                    lcd_init_cmds[cmd_count].data_bytes, false);
    } else {
      vTaskDelay(pdMS_TO_TICKS(lcd_init_cmds[cmd_count].data[0]));
    }
    cmd_count += 1;
  }
  lcd_display_work_state = true;
  ESP_ERROR_CHECK(spi_lcd_master_deconfig());
}

#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
/**
 * @brief Sleep lcd display rgb driver.
 *
 */
void lcd_display_rgb_driver_sleep(void) {
  lcd_display_gpio_reset_timing();
  ESP_ERROR_CHECK(spi_lcd_master_config(LCD_DISPLAY_SCLK, LCD_DISPLAY_MOSI,
                                        LCD_DISPLAY_MISO, LCD_DISPLAY_CS,
                                        LCD_DISPLAY_DC));
  uint8_t cmd_count = 0;
  /* TODO: June6 读取芯片ID */
  while (lcd_sleep_cmds[cmd_count].data_bytes != 0xFF) {
    if ((lcd_sleep_cmds[cmd_count].cmd != 0) ||
        (lcd_sleep_cmds[cmd_count].data_bytes != 0)) {
      spi_lcd_write(true, &(lcd_sleep_cmds[cmd_count].cmd), 1, false);
      spi_lcd_write(false, lcd_sleep_cmds[cmd_count].data,
                    lcd_sleep_cmds[cmd_count].data_bytes, false);
    } else {
      vTaskDelay(pdMS_TO_TICKS(lcd_sleep_cmds[cmd_count].data[0]));
    }
    cmd_count += 1;
  }
  lcd_display_work_state = false;
  lcd_display_gpio_sleep_timing();
  ESP_ERROR_CHECK(spi_lcd_master_deconfig());
}
#endif
