#include "Detect.h"
#include "Display.h"
#include "Ili9881cDisplay.h"
#include "St7123Display.h"
#include "St7123Touch.h"

#include <Gt911Touch.h>
#include <PwmBacklight.h>
#include <Tactility/hal/gpio/Gpio.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

constexpr auto* TAG = "Tab5Display";

// LCD reset is wired to the PI4IOE5V6408 IO expander (io_expander0, bit 4), pulsed in
// Configuration.cpp's initExpander0() before display creation - not a direct SoC GPIO.
constexpr auto LCD_PIN_RESET = GPIO_NUM_NC;
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_22;

static std::shared_ptr<tt::hal::touch::TouchDevice> createGt911Touch() {
    auto* i2c = device_find_by_name("i2c0");
    check(i2c);
    auto configuration = std::make_unique<Gt911Touch::Configuration>(
        i2c,
        720,
        1280,
        false,  // swapXY
        false,  // mirrorX
        false,  // mirrorY
        GPIO_NUM_NC, // reset pin
        GPIO_NUM_NC  // "GPIO_NUM_23 cannot be used due to resistor to 3V3"
                     // https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L76234
    );
    return std::make_shared<Gt911Touch>(std::move(configuration));
}

static std::shared_ptr<tt::hal::touch::TouchDevice> createSt7123Touch() {
    auto* i2c = device_find_by_name("i2c0");
    check(i2c, "i2c0 not found");
    auto configuration = std::make_unique<St7123Touch::Configuration>(
        i2c,
        720,
        1280,
        false,       // swapXY
        false,       // mirrorX
        false,       // mirrorY
        GPIO_NUM_23  // interrupt pin
    );
    return std::make_shared<St7123Touch>(std::move(configuration));
}

std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
    // Initialize PWM backlight
    if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 5000, LEDC_TIMER_1, LEDC_CHANNEL_0)) {
        LOG_W(TAG, "Failed to initialize backlight");
    }

    Tab5Variant variant = detectVariant();

    std::shared_ptr<tt::hal::touch::TouchDevice> touch;

    if (variant == Tab5Variant::St7123) {
        touch = createSt7123Touch();
    } else {
        touch = createGt911Touch();

        // Work-around to init GT911 touch: interrupt pin must be set to low
        // Note: There is a resistor to 3V3 on interrupt pin which is blocking GT911 touch
        // See https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L777
        tt::hal::gpio::configure(23, tt::hal::gpio::Mode::Output, true, false);
        tt::hal::gpio::setLevel(23, false);
    }

    auto configuration = std::make_shared<EspLcdConfiguration>(EspLcdConfiguration {
        .horizontalResolution = 720,
        .verticalResolution = 1280,
        .gapX = 0,
        .gapY = 0,
        .monochrome = false,
        .swapXY = false,
        .mirrorX = false,
        .mirrorY = false,
        .invertColor = false,
        .bufferSize = 0, // 0 = default (1/10 of screen)
        .swRotate = true,
        .buffSpiram = true,
        .touch = touch,
        .backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
        .resetPin = LCD_PIN_RESET,
        .lvglColorFormat = LV_COLOR_FORMAT_RGB565,
        .lvglSwapBytes = false,
        .rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB,
        .bitsPerPixel = 16
    });

    if (variant == Tab5Variant::St7123) {
        return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
            std::make_shared<St7123Display>(configuration)
        );
    } else {
        return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
            std::make_shared<Ili9881cDisplay>(configuration)
        );
    }
}
