#include "Display.hpp"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_ldo_regulator.h"
#include "esp_lcd_jd9365_10_1.h"
#include "esp_lv_adapter.h"

// Display config
#define LCD_H_RES 800
#define LCD_V_RES 1280
#define LCD_BIT_PER_PIXEL 16  // RGB565 for better performance
#define LCD_RST_GPIO 27
#define LCD_BK_GPIO 26
#define LCD_BK_ON_LEVEL 1

// MIPI DSI config
#define MIPI_DSI_LANE_NUM 2
#define MIPI_DSI_PHY_LDO_CHAN 3
#define MIPI_DSI_PHY_LDO_VOLTAGE_MV 2500

Display::Display() : panel_handle(nullptr) {}

void Display::init() {
    // --- Backlight Init ---
    gpio_config_t bk_gpio_config = {};
    bk_gpio_config.mode = GPIO_MODE_OUTPUT;
    bk_gpio_config.pin_bit_mask = 1ULL << LCD_BK_GPIO;
    gpio_config(&bk_gpio_config);
    gpio_set_level(static_cast<gpio_num_t>(LCD_BK_GPIO), LCD_BK_ON_LEVEL);

    // --- DSI PHY LDO Init ---
    esp_ldo_channel_handle_t ldo_mipi = NULL;
    esp_ldo_channel_config_t ldo_cfg = {};
    ldo_cfg.chan_id = MIPI_DSI_PHY_LDO_CHAN;
    ldo_cfg.voltage_mv = MIPI_DSI_PHY_LDO_VOLTAGE_MV;
    esp_ldo_acquire_channel(&ldo_cfg, &ldo_mipi);

    // --- DSI Bus Init ---
    esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
    esp_lcd_dsi_bus_config_t bus_cfg = {};
    bus_cfg.bus_id = 0;
    bus_cfg.num_data_lanes = 2;
    bus_cfg.phy_clk_src = static_cast<mipi_dsi_phy_pllref_clock_source_t>(0);
    bus_cfg.lane_bit_rate_mbps = 1500;
    esp_lcd_new_dsi_bus(&bus_cfg, &mipi_dsi_bus);

    // --- DSI Panel IO Init ---
    esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
    esp_lcd_dbi_io_config_t io_cfg = {};
    io_cfg.virtual_channel = 0;
    io_cfg.lcd_cmd_bits = 8;
    io_cfg.lcd_param_bits = 8;
    esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &io_cfg, &mipi_dbi_io);

    // --- DPI Panel Config ---
    esp_lcd_dpi_panel_config_t dpi_cfg = {};
    dpi_cfg.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT;
    dpi_cfg.dpi_clock_freq_mhz = 80;
    dpi_cfg.virtual_channel = 0;
    dpi_cfg.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565;
    dpi_cfg.video_timing.h_size = 800;
    dpi_cfg.video_timing.v_size = 1280;
    dpi_cfg.video_timing.hsync_back_porch = 20;
    dpi_cfg.video_timing.hsync_pulse_width = 20;
    dpi_cfg.video_timing.hsync_front_porch = 40;
    dpi_cfg.video_timing.vsync_back_porch = 10;
    dpi_cfg.video_timing.vsync_pulse_width = 4;
    dpi_cfg.video_timing.vsync_front_porch = 30;
    dpi_cfg.flags.use_dma2d = true;
    dpi_cfg.num_fbs = esp_lv_adapter_get_required_frame_buffer_count(
        ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_MIPI_DSI,
        ESP_LV_ADAPTER_ROTATE_90
    );

    // --- Vendor Config ---
    jd9365_vendor_config_t vendor_cfg = {};
    vendor_cfg.flags.use_mipi_interface = 1;
    vendor_cfg.mipi_config.dsi_bus = mipi_dsi_bus;
    vendor_cfg.mipi_config.dpi_config = &dpi_cfg;
    vendor_cfg.mipi_config.lane_num = MIPI_DSI_LANE_NUM;

    // --- Panel Device Config ---
    esp_lcd_panel_dev_config_t panel_cfg = {};
    panel_cfg.reset_gpio_num = LCD_RST_GPIO;
    panel_cfg.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
    panel_cfg.bits_per_pixel = LCD_BIT_PER_PIXEL;
    panel_cfg.vendor_config = &vendor_cfg;

    esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_cfg, &this->panel_handle);
    
    esp_lcd_panel_reset(this->panel_handle);
    esp_lcd_panel_init(this->panel_handle);
    esp_lcd_panel_disp_on_off(this->panel_handle, true);
}

esp_lcd_panel_handle_t Display::getPanelHandle() const {
    return this->panel_handle;
}
