#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"

#include "i2c.h"

#define LVGL_PALETTE_SIZE      8

class IPanelDevice {
public:
  explicit IPanelDevice(I2C &i2c,
                        esp_lcd_panel_io_i2c_config_t config,
                        int width,
                        int height) :
      IPanelDevice(i2c, config, width, height,
                   width * height / 8 + LVGL_PALETTE_SIZE) { }

  explicit IPanelDevice(I2C &i2c,
                        esp_lcd_panel_io_i2c_config_t io_config,
                        int width,
                        int height,
                        size_t draw_buf_size) :
      width_(width),
      height_(height),
      rst_num_(i2c.rst_num_),
      lv_buf_size_(draw_buf_size),
      i2c_bus_(i2c.i2c_bus_),
      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 rst_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
