#include "display.h"

#include <stddef.h>
#include <stdio.h>
#include <string.h>

#include "ili9341.h"

#define CHAR_W_SMALL 6
#define CHAR_H_SMALL 8
#define CHAR_W_LARGE 12
#define CHAR_H_LARGE 16

static bool s_inited = false;

static int text_width_px(const char *text, int char_w)
{
    if (text == NULL) {
        return 0;
    }
    return (int)strlen(text) * char_w;
}

esp_err_t display_init(void)
{
    if (s_inited) {
        return ESP_OK;
    }

    ESP_ERROR_CHECK(ili9341_init());
    s_inited = true;
    return display_clear(RGB565_BLACK);
}

esp_err_t display_fill_rect(int x, int y, int w, int h, uint16_t color)
{
    if (!s_inited) {
        return ESP_ERR_INVALID_STATE;
    }

    if (w <= 0 || h <= 0) {
        return ESP_OK;
    }

    if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
        return ESP_OK;
    }

    if (x < 0) {
        w += x;
        x = 0;
    }
    if (y < 0) {
        h += y;
        y = 0;
    }

    if (x + w > DISPLAY_WIDTH) {
        w = DISPLAY_WIDTH - x;
    }
    if (y + h > DISPLAY_HEIGHT) {
        h = DISPLAY_HEIGHT - y;
    }

    if (w <= 0 || h <= 0) {
        return ESP_OK;
    }

    for (int py = y; py < y + h; ++py) {
        for (int px = x; px < x + w; ++px) {
            ESP_ERROR_CHECK(ili9341_draw_pixel((uint16_t)px, (uint16_t)py, color));
        }
    }

    return ESP_OK;
}

esp_err_t display_clear(uint16_t color)
{
    if (!s_inited) {
        return ESP_ERR_INVALID_STATE;
    }

    ili9341_fill_screen(color);
    return ESP_OK;
}

esp_err_t display_draw_text_small(int x, int y, const char *text, uint16_t fg, uint16_t bg)
{
    if (!s_inited) {
        return ESP_ERR_INVALID_STATE;
    }
    if (text == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    ili9341_draw_text(x, y, text, fg, bg);
    return ESP_OK;
}

esp_err_t display_draw_text(int x, int y, const char *text, uint16_t fg, uint16_t bg)
{
    if (!s_inited) {
        return ESP_ERR_INVALID_STATE;
    }
    if (text == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    ili9341_draw_text(x, y, text, fg, bg);
    return ESP_OK;
}

esp_err_t display_draw_text_centered(int y, const char *text, uint16_t fg, uint16_t bg)
{
    int w = text_width_px(text, CHAR_W_LARGE);
    int x = (DISPLAY_WIDTH - w) / 2;
    if (x < 0) {
        x = 0;
    }
    return display_draw_text(x, y, text, fg, bg);
}

esp_err_t display_draw_text_small_centered(int y, const char *text, uint16_t fg, uint16_t bg)
{
    int w = text_width_px(text, CHAR_W_SMALL);
    int x = (DISPLAY_WIDTH - w) / 2;
    if (x < 0) {
        x = 0;
    }
    return display_draw_text_small(x, y, text, fg, bg);
}

esp_err_t display_draw_text_inverse(int x, int y, const char *text, uint16_t bg, uint16_t fg)
{
    return display_draw_text_small(x, y, text, fg, bg);
}

esp_err_t display_draw_button_status(int btn_id, bool pressed)
{
    if (!s_inited) {
        return ESP_ERR_INVALID_STATE;
    }

    const int slot_w = DISPLAY_WIDTH / 3;
    const int slot_h = 22;
    const int x = btn_id * slot_w;
    const int y = DISPLAY_HEIGHT - slot_h;

    uint16_t fill = pressed ? RGB565_RED : RGB565_DIM_GRAY;
    uint16_t text = RGB565_WHITE;

    ESP_ERROR_CHECK(display_fill_rect(x, y, slot_w - 1, slot_h - 1, fill));

    char label[8];
    snprintf(label, sizeof(label), "D%d", btn_id);
    ESP_ERROR_CHECK(display_draw_text_small(x + (slot_w / 2) - 6, y + 7, label, text, fill));

    return ESP_OK;
}
