#include "Fonts.hpp"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <cstddef>

#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
#include "esp_lv_adapter.h"
#include <sys/stat.h>
#endif

namespace {
static const char* TAG = "Fonts";
static constexpr const char* kFontPath = "/sdcard/fonts/Montserrat-Medium.ttf";
static constexpr const char* kIconFontPath = "/sdcard/fonts/MaterialSymbolsOutlined.ttf";
static constexpr uint16_t kFontSizes[] = {14, 18, 22, 28, 36, 48};
static constexpr size_t kFontCount = sizeof(kFontSizes) / sizeof(kFontSizes[0]);

// Icon fonts have more sizes including large ones for decorative use
// Large icons (>150px) use PSRAM for draw buffers via CLIB_MALLOC
static constexpr uint16_t kIconFontSizes[] = {14, 18, 22, 28, 36, 48, 64, 80, 96, 120, 150, 180, 220, 260};
static constexpr size_t kIconFontCount = sizeof(kIconFontSizes) / sizeof(kIconFontSizes[0]);

static const lv_font_t* s_fonts[kFontCount] = {nullptr};
static const lv_font_t* s_iconFonts[kIconFontCount] = {nullptr};
static bool s_initialized = false;
static bool s_iconFontAvailable = false;

const lv_font_t* fallbackFont(uint8_t sizeIndex) {
    switch (sizeIndex) {
        case 0: return &lv_font_montserrat_14;
#if LV_FONT_MONTSERRAT_18
        case 1: return &lv_font_montserrat_18;
#endif
#if LV_FONT_MONTSERRAT_22
        case 2: return &lv_font_montserrat_22;
#endif
#if LV_FONT_MONTSERRAT_28
        case 3: return &lv_font_montserrat_28;
#endif
#if LV_FONT_MONTSERRAT_36
        case 4: return &lv_font_montserrat_36;
#endif
#if LV_FONT_MONTSERRAT_48
        case 5: return &lv_font_montserrat_48;
#endif
        default: return &lv_font_montserrat_14;
    }
}

#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
bool fontFileExists(const char* path) {
    struct stat st;
    return stat(path, &st) == 0;
}
#endif
} // namespace

void Fonts::init() {

#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
    ESP_LOGI(TAG, "FreeType enabled");
    if (s_initialized) {
        ESP_LOGI(TAG, "Already initialized");
        return;
    }
    s_initialized = true;

    if (!fontFileExists(kFontPath)) {
        ESP_LOGW(TAG, "Font file not found: %s", kFontPath);
        return;
    }
    ESP_LOGI(TAG, "Font file exists: %s", kFontPath);

    // Give LVGL task time to start and release mutex
    ESP_LOGI(TAG, "Waiting for LVGL task...");
    vTaskDelay(pdMS_TO_TICKS(500));

    // Test if LVGL lock is available
    ESP_LOGI(TAG, "Testing LVGL lock...");
    if (esp_lv_adapter_lock(1000) == ESP_OK) {  // 1 second timeout
        ESP_LOGI(TAG, "Lock acquired OK, releasing...");
        esp_lv_adapter_unlock();
    } else {
        ESP_LOGE(TAG, "Could not acquire LVGL lock - something is holding it!");
        return;  // Don't try to init fonts if lock is stuck
    }

    for (size_t i = 0; i < kFontCount; ++i) {
        ESP_LOGI(TAG, "Loading font size %u...", kFontSizes[i]);
        esp_lv_adapter_ft_font_config_t cfg = {};
        cfg.name = kFontPath;
        cfg.size = kFontSizes[i];
        cfg.style = ESP_LV_ADAPTER_FT_FONT_STYLE_NORMAL;
        cfg.mem = nullptr;
        cfg.mem_size = 0;

        esp_lv_adapter_ft_font_handle_t handle = nullptr;
        ESP_LOGI(TAG, "Calling esp_lv_adapter_ft_font_init (free heap: %lu)...",
                 (unsigned long)esp_get_free_heap_size());
        esp_err_t ret = esp_lv_adapter_ft_font_init(&cfg, &handle);
        ESP_LOGI(TAG, "esp_lv_adapter_ft_font_init returned %d", ret);
        if (ret != ESP_OK || handle == nullptr) {
            ESP_LOGW(TAG, "Failed to load FreeType font size %u (err=%d)", cfg.size, ret);
            continue;
        }

        s_fonts[i] = esp_lv_adapter_ft_font_get(handle);
        if (!s_fonts[i]) {
            ESP_LOGW(TAG, "FreeType font handle returned null for size %u", cfg.size);
        }
        ESP_LOGI(TAG, "Font size %u loaded successfully", kFontSizes[i]);
    }
    ESP_LOGI(TAG, "Text font initialization complete");

    // Load icon font if available (includes larger sizes for decorative icons)
    if (fontFileExists(kIconFontPath)) {
        ESP_LOGI(TAG, "Icon font file exists: %s", kIconFontPath);
        for (size_t i = 0; i < kIconFontCount; ++i) {
            ESP_LOGI(TAG, "Loading icon font size %u...", kIconFontSizes[i]);
            esp_lv_adapter_ft_font_config_t cfg = {};
            cfg.name = kIconFontPath;
            cfg.size = kIconFontSizes[i];
            cfg.style = ESP_LV_ADAPTER_FT_FONT_STYLE_NORMAL;
            cfg.mem = nullptr;
            cfg.mem_size = 0;

            esp_lv_adapter_ft_font_handle_t handle = nullptr;
            esp_err_t ret = esp_lv_adapter_ft_font_init(&cfg, &handle);
            if (ret != ESP_OK || handle == nullptr) {
                ESP_LOGW(TAG, "Failed to load icon font size %u (err=%d)", cfg.size, ret);
                continue;
            }

            s_iconFonts[i] = esp_lv_adapter_ft_font_get(handle);
            if (s_iconFonts[i]) {
                s_iconFontAvailable = true;
                ESP_LOGI(TAG, "Icon font size %u loaded successfully", kIconFontSizes[i]);
            }
        }
        ESP_LOGI(TAG, "Icon font initialization complete (%zu sizes)", kIconFontCount);
    } else {
        ESP_LOGW(TAG, "Icon font file not found: %s", kIconFontPath);
    }
#else
    ESP_LOGI(TAG, "FreeType disabled, using built-in fonts");
#endif
}

const lv_font_t* Fonts::bySizeIndex(uint8_t sizeIndex) {
#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
    if (sizeIndex < kFontCount && s_fonts[sizeIndex]) {
        return s_fonts[sizeIndex];
    }
#endif
    return fallbackFont(sizeIndex);
}

const lv_font_t* Fonts::iconFont(uint8_t sizeIndex) {
#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
    if (sizeIndex < kIconFontCount && s_iconFonts[sizeIndex]) {
        return s_iconFonts[sizeIndex];
    }
    // Try to return any available icon font size as fallback
    if (s_iconFontAvailable) {
        for (size_t i = 0; i < kIconFontCount; ++i) {
            if (s_iconFonts[i]) return s_iconFonts[i];
        }
    }
#endif
    // No icon font available - return text font as last resort
    return fallbackFont(sizeIndex < kFontCount ? sizeIndex : kFontCount - 1);
}

bool Fonts::hasIconFont() {
#if CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE
    return s_iconFontAvailable;
#else
    return false;
#endif
}
