#include "app_task.h"

#include "analytics.h"
#include "button_handling.h"
#include "common/InactivityTracker.h"
#include "hal/u8g2_esp32_hal.h"
#include "hal_esp32/PersistenceManager.h"
#include "i2c_checker.h"
#include "led_status.h"
#include "simulator.h"
#include "ui/ClockScreenSaver.h"
#include "ui/ScreenSaver.h"
#include "ui/SplashScreen.h"
#include "wifi_manager.h"
#include <driver/i2c.h>
#include <esp_diagnostics.h>
#include <esp_log.h>
#include <esp_task_wdt.h>
#include <esp_timer.h>
#include <sdkconfig.h>
#include <u8g2.h>

#define PIN_RST GPIO_NUM_NC

static const char *TAG = "app_task";

u8g2_t u8g2;
uint8_t last_value = 0;
menu_options_t options;
uint8_t received_signal;

std::shared_ptr<Widget> m_widget;
std::vector<std::shared_ptr<Widget>> m_history;
std::unique_ptr<InactivityTracker> m_inactivityTracker;
std::shared_ptr<PersistenceManager> m_persistenceManager;

extern QueueHandle_t buttonQueue;

static void setup_screen(void)
{
    u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
    u8g2_esp32_hal.bus.i2c.sda = I2C_MASTER_SDA_PIN;
    u8g2_esp32_hal.bus.i2c.scl = I2C_MASTER_SCL_PIN;
    u8g2_esp32_hal.reset = PIN_RST;
    u8g2_esp32_hal_init(u8g2_esp32_hal);

    u8g2_Setup_sh1106_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);
    u8x8_SetI2CAddress(&u8g2.u8x8, DISPLAY_I2C_ADDRESS << 1);

    ESP_DIAG_EVENT(TAG, "u8g2_InitDisplay");
    u8g2_InitDisplay(&u8g2);
    vTaskDelay(pdMS_TO_TICKS(10));

    ESP_DIAG_EVENT(TAG, "u8g2_SetPowerSave");
    u8g2_SetPowerSave(&u8g2, 0);
    vTaskDelay(pdMS_TO_TICKS(10));

    u8g2_ClearDisplay(&u8g2);
}

void setScreen(const std::shared_ptr<Widget> &screen)
{
    if (screen != nullptr)
    {
        m_widget = screen;
        m_history.clear();
        m_history.emplace_back(m_widget);
        m_widget->onEnter();
    }
}

void pushScreen(const std::shared_ptr<Widget> &screen)
{
    if (screen != nullptr)
    {
        if (m_widget)
        {
            m_widget->onPause();
        }
        m_widget = screen;
        m_widget->onEnter();
        m_history.emplace_back(m_widget);
    }
}

void popScreen()
{
    if (m_history.size() >= 2)
    {
        m_history.pop_back();
        if (m_widget)
        {
            if (m_persistenceManager != nullptr)
            {
                m_persistenceManager->Save();
            }
            m_widget->onExit();
        }
        m_widget = m_history.back();
        m_widget->onResume();
    }
}

static void init_ui(void)
{
    m_persistenceManager = std::make_shared<PersistenceManager>();
    options = {
        .u8g2 = &u8g2,
        .setScreen = [](const std::shared_ptr<Widget> &screen) { setScreen(screen); },
        .pushScreen = [](const std::shared_ptr<Widget> &screen) { pushScreen(screen); },
        .popScreen = []() { popScreen(); },
        .onButtonClicked = nullptr,
        .persistenceManager = m_persistenceManager,
    };
    m_widget = std::make_shared<SplashScreen>(&options);
    m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
        auto screensaver = std::make_shared<ClockScreenSaver>(&options);
        options.pushScreen(screensaver);
    });

    u8g2_ClearBuffer(&u8g2);
    m_widget->Render();
    u8g2_SendBuffer(&u8g2);
}

static void handle_button(uint8_t button)
{
    m_inactivityTracker->reset();

    if (m_widget)
    {
        switch (button)
        {
        case 1:
            m_widget->OnButtonClicked(ButtonType::UP);
            break;

        case 3:
            m_widget->OnButtonClicked(ButtonType::LEFT);
            break;

        case 5:
            m_widget->OnButtonClicked(ButtonType::RIGHT);
            break;

        case 6:
            m_widget->OnButtonClicked(ButtonType::DOWN);
            break;

        case 16:
            m_widget->OnButtonClicked(ButtonType::BACK);
            break;

        case 18:
            m_widget->OnButtonClicked(ButtonType::SELECT);
            break;

        default:
            ESP_LOGE(TAG, "Unhandled button: %u", button);
            break;
        }
    }
}

void app_task(void *args)
{
    esp_task_wdt_add(NULL);

    if (i2c_bus_scan_and_check() != ESP_OK)
    {
        led_behavior_t led0_behavior = {.mode = LED_MODE_BLINK,
                                        .color = {.red = 50, .green = 0, .blue = 0},
                                        .on_time_ms = 1000,
                                        .off_time_ms = 500};
        led_status_set_behavior(0, led0_behavior);

        ESP_LOGE(TAG, "Display not found on I2C bus");
        vTaskDelete(nullptr);
        return;
    }

    setup_screen();
    setup_buttons();
    init_ui();

#if CONFIG_WIFI_ENABLED
    wifi_manager_init();
    analytics_init();
#endif

    start_simulation();

    auto oldTime = esp_timer_get_time();

    while (true)
    {
        esp_task_wdt_reset();

        u8g2_ClearBuffer(&u8g2);

        if (m_widget != nullptr)
        {
            auto currentTime = esp_timer_get_time();
            auto delta = currentTime - oldTime;
            oldTime = currentTime;

            uint64_t deltaMs = delta / 1000;

            m_widget->Update(deltaMs);
            m_widget->Render();

            m_inactivityTracker->update(deltaMs);
        }

        u8g2_SendBuffer(&u8g2);

        if (xQueueReceive(buttonQueue, &received_signal, pdMS_TO_TICKS(10)) == pdTRUE)
        {
            handle_button(received_signal);
        }
    }

    cleanup_buttons();
}
