/* ota_display.c — OTA notification/progress UI on GC9A01 (bd-1yr8)
 *
 * Renders OTA state overlaid on the 240×240 round HUD display:
 *   - BADGE: small dot on top-right when update available (idle state)
 *   - UPDATE SCREEN: version compare, Update Balance / Update IO / Update All
 *   - PROGRESS: arc around display perimeter + % + status text
 *   - ERROR: red banner + "RETRY" prompt
 *
 * The display_draw_* primitives must be provided by the GC9A01 driver.
 * Actual SPI driver implementation is in a separate driver bead.
 */

#include "ota_display.h"
#include "gc9a01.h"
#include "gitea_ota.h"
#include "vesc_can.h"
#include "version.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdio.h>
#include <string.h>

static const char *TAG = "ota_disp";

/* Display centre and radius for the 240×240 GC9A01 */
#define CX   120
#define CY   120
#define RAD  110

/* "SAULT" at scale=4: 5 chars × 24px wide = 120px, 28px tall */
#define SAULT_SCALE  4
#define SAULT_X      60   /* (240 - 120) / 2 */
#define SAULT_Y      86   /* centre vertically */

/* Battery voltage line at scale=2: "XX.XV" ≤ 5 chars × 12px = 60px */
#define VBAT_SCALE   2
#define VBAT_Y       130

static bool s_hud_dirty = true;  /* set true whenever OTA overlay was active */

/* ── Idle HUD: SAULT title + battery voltage ── */
static void draw_hud_idle(void)
{
    if (s_hud_dirty) {
        /* Clear full screen on first entry after OTA overlay */
        display_fill_rect(0, 0, 240, 240, COL_BG);
        display_draw_string_s(SAULT_X, SAULT_Y, "SAULT", COL_WHITE, COL_BG, SAULT_SCALE);
        s_hud_dirty = false;
    }
    /* Update battery voltage every tick */
    uint16_t vbat_mv = (uint16_t)((int32_t)g_vesc[0].voltage_x10 * 100);
    char vbuf[16];
    if (vbat_mv == 0) {
        snprintf(vbuf, sizeof(vbuf), "--.-V");
    } else {
        snprintf(vbuf, sizeof(vbuf), "%2u.%uV", vbat_mv / 1000u, (vbat_mv % 1000u) / 100u);
    }
    int vx = CX - (int)(strlen(vbuf) * 6 * VBAT_SCALE / 2);
    display_fill_rect(vx - 2, VBAT_Y - 2, (int)(strlen(vbuf) * 6 * VBAT_SCALE) + 4,
                      7 * VBAT_SCALE + 4, COL_BG);
    display_draw_string_s(vx, VBAT_Y, vbuf, COL_GREEN, COL_BG, VBAT_SCALE);
}

/* ── Availability badge: 8×8 dot at top-right of display ── */
static void draw_badge(bool balance_avail, bool io_avail)
{
    uint16_t col = (balance_avail || io_avail) ? COL_ORANGE : COL_BG;
    display_fill_rect(200, 15, 12, 12, col);
}

/* ── Progress arc: sweeps 0→360° proportional to progress% ── */
static void draw_progress_arc(uint8_t pct, uint16_t color)
{
    int end_deg = (int)(360 * pct / 100);
    display_draw_arc(CX, CY, RAD, 0, end_deg, 6, color);
}

/* ── Status banner: 2 lines of text centred on display ── */
static void draw_status(const char *line1, const char *line2,
                         uint16_t fg, uint16_t bg)
{
    display_fill_rect(20, 90, 200, 60, bg);
    if (line1 && line1[0])
        display_draw_string(CX - (int)(strlen(line1) * 6 / 2), 96,
                             line1, fg, bg);
    if (line2 && line2[0])
        display_draw_string(CX - (int)(strlen(line2) * 6 / 2), 116,
                             line2, fg, bg);
}

/* ── Main render logic ── */
void ota_display_update(void)
{
    /* Determine dominant OTA state */
    ota_self_state_t  self  = g_ota_self_state;
    uart_ota_send_state_t io_s = g_uart_ota_state;

    switch (self) {
    case OTA_SELF_DOWNLOADING:
    case OTA_SELF_VERIFYING:
    case OTA_SELF_APPLYING: {
        s_hud_dirty = true;
        char pct_str[16];
        snprintf(pct_str, sizeof(pct_str), "%d%%", g_ota_self_progress);
        const char *phase = (self == OTA_SELF_VERIFYING) ? "Verifying..." :
                            (self == OTA_SELF_APPLYING)  ? "Applying..." :
                                                            "Downloading...";
        draw_progress_arc(g_ota_self_progress, COL_BLUE);
        draw_status("Updating Balance", pct_str, COL_WHITE, COL_BG);
        ESP_LOGD(TAG, "balance OTA %s %d%%", phase, g_ota_self_progress);
        return;
    }
    case OTA_SELF_REBOOTING:
        s_hud_dirty = true;
        draw_status("Update complete", "Rebooting...", COL_GREEN, COL_BG);
        return;
    case OTA_SELF_FAILED:
        s_hud_dirty = true;
        draw_progress_arc(0, COL_RED);
        draw_status("Balance update", "FAILED  RETRY?", COL_RED, COL_BG);
        return;
    default:
        break;
    }

    switch (io_s) {
    case UART_OTA_S_DOWNLOADING:
        s_hud_dirty = true;
        draw_progress_arc(g_uart_ota_progress, COL_YELLOW);
        draw_status("Downloading IO", "firmware...", COL_WHITE, COL_BG);
        return;
    case UART_OTA_S_SENDING: {
        s_hud_dirty = true;
        char pct_str[16];
        snprintf(pct_str, sizeof(pct_str), "%d%%", g_uart_ota_progress);
        draw_progress_arc(g_uart_ota_progress, COL_YELLOW);
        draw_status("Updating IO", pct_str, COL_WHITE, COL_BG);
        return;
    }
    case UART_OTA_S_DONE:
        s_hud_dirty = true;
        draw_status("IO update done", "", COL_GREEN, COL_BG);
        return;
    case UART_OTA_S_FAILED:
        s_hud_dirty = true;
        draw_progress_arc(0, COL_RED);
        draw_status("IO update", "FAILED  RETRY?", COL_RED, COL_BG);
        return;
    default:
        break;
    }

    /* Idle — draw SAULT HUD + badge */
    bool bal_avail = g_balance_update.available;
    bool io_avail  = g_io_update.available;
    draw_hud_idle();
    draw_badge(bal_avail, io_avail);

    if (bal_avail || io_avail) {
        char verline[48];
        if (bal_avail) {
            snprintf(verline, sizeof(verline), "Bal v%s rdy",
                     g_balance_update.version);
            draw_status(verline, io_avail ? "IO update rdy" : "",
                         COL_ORANGE, COL_BG);
        } else if (io_avail) {
            snprintf(verline, sizeof(verline), "IO v%s rdy",
                     g_io_update.version);
            draw_status(verline, "", COL_ORANGE, COL_BG);
        }
    }
}

/* ── Background display task (5 Hz) ── */
static void ota_display_task(void *arg)
{
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(200));
        ota_display_update();
    }
}

void ota_display_init(void)
{
    xTaskCreate(ota_display_task, "ota_disp", 2048, NULL, 3, NULL);
    ESP_LOGI(TAG, "OTA display task started");
}
