#ifndef PANEL_DEVICE_H
#define PANEL_DEVICE_H

#include <esp_lcd_panel_dev.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_ssd1306.h>
#include <driver/i2c_types.h>
#include <esp_lcd_panel_io.h>
#include <esp_log.h>
#include "display/lv_display.h"

#define LVGL_PALETTE_SIZE      8

static const char *TAG = "lcd-panel";

class IPanelDevice {
public:
  explicit IPanelDevice(i2c_master_bus_handle_t i2c,
                        int reset_gpio_num,
                        esp_lcd_panel_io_i2c_config_t io_config) :
      reset_gpio_num_(reset_gpio_num),
      i2c_bus_(i2c),
      io_config_(io_config) { }

  virtual ~IPanelDevice() = default;

  [[nodiscard]] lv_display_t *create_display() const
  {
    auto display = lv_display_create(width_, height_);
    assert(display);
    return display;
  }

  void create_panel(esp_lcd_panel_dev_config_t &config,
                    esp_lcd_panel_io_handle_t io,
                    esp_lcd_panel_handle_t &panel)
  {
    // If the passed handle is already allocated, delete it.
    if (panel != nullptr) {
      ESP_LOGI(TAG, "Removing unused panel");
      esp_lcd_panel_del(panel);
    }

    ESP_LOGI(TAG, "Install SSD1306 panel driver");
    init_panel(config, io, panel);
  }

  int32_t width_;
  int32_t height_;
  int reset_gpio_num_;

  // LVGL reserves 2x4 bytes in the buffer to be used as a palette.
  size_t lv_buf_size_;
  // TODO: Can we use a static accessor in I2C instead?
  i2c_master_bus_handle_t i2c_bus_;

  esp_lcd_panel_io_i2c_config_t io_config_;

private:
  virtual void init_panel(esp_lcd_panel_dev_config_t &config,
                          esp_lcd_panel_io_handle_t io,
                          esp_lcd_panel_handle_t &panel) = 0;
};

#endif // PANEL_DEVICE_H
