#include "pico/stdlib.h" // Подключаем базовые функции SDK Pico (время, таймеры, sleep, assert).
#include "display/display.h" // Подключаем API дисплейного движка (init + begin/end paint).
#include "display/render/context.h" // Подключаем контекст рисования для примитивов.
#include "display/render/grid.h" // Подключаем функцию рисования сетки.
#include "display/render/sine_wave.h" // Подключаем функцию рисования синусоиды.
#include "Font/font_data.h" // Подключаем шрифты и draw_string.
#include <wchar.h> // Подключаем swprintf для форматирования строки FPS.


#define WIDTH   320 // Ширина экрана в пикселях.
#define HEIGHT  240 // Высота экрана в пикселях.
#define TEXT_H  22 // Высота глифа шрифта для расчета нижней границы текста.

static inline void wait_for_irq(void) // Вспомогательная функция для энергосберегающего ожидания прерываний.
{ 
    __asm volatile ("wfi"); // Инструкция ARM Wait For Interrupt: CPU спит до любого IRQ.
} 

static uint16_t text_width_px(const wchar_t* str) // Считаем фактическую ширину строки в пикселях через таблицу ширин глифов.
{ 
    uint16_t width = 0; // Накапливаем итоговую ширину строки.
    while (*str) // Пока не дошли до нулевого терминатора строки.
    { 
        width = (uint16_t)(width + get_char_width(*str)); // Добавляем ширину текущего символа.
        str++; // Переходим к следующему символу.
    } 
    return width; // Возвращаем итоговую ширину строки.
} 

static void update_axis_reflect(int* pos, int* dir, int min_pos, int max_pos) // Обновляем одну координату с зеркальным отражением от границ.
{ 
    if (max_pos <= min_pos) // Если объект больше доступного диапазона или диапазон вырожден.
    { 
        *pos = min_pos; // Фиксируем позицию в единственной доступной точке.
        *dir = 0; // Останавливаем движение по этой оси.
        return; // Завершаем обработку оси.
    } 

    *pos += *dir; // Двигаем объект на 1 пиксель по текущему направлению.
    if (*pos < min_pos) // Проверяем удар о минимальную границу (верх/лево).
    { 
        *pos = min_pos + (min_pos - *pos); // Зеркально отражаем координату внутрь диапазона.
        *dir = -*dir; // Инвертируем направление движения.
    } 
    else if (*pos > max_pos) // Проверяем удар о максимальную границу (низ/право).
    { 
        *pos = max_pos - (*pos - max_pos); // Зеркально отражаем координату внутрь диапазона.
        *dir = -*dir; // Инвертируем направление движения.
    } 
} 

int main() 
{ 
    stdio_init_all(); // Инициализируем стандартный ввод/вывод (UART/USB stdio при необходимости).

    display_config_t cfg = { // Формируем конфигурацию дисплейного движка.
        .width  = WIDTH, // Передаем ширину кадра.
        .height = HEIGHT, // Передаем высоту кадра.
        .buffer_count = 1, // Используем два буфера кадра (SAFE режим блокирует доступ при DMA).
        .mode = DISPLAY_MODE_SAFE, // Выбираем безопасный режим работы буфера.
    };

    display_init(&cfg); // Инициализируем дисплей и внутренний контекст по заданной конфигурации.
    float phase = 0.0f; // Фаза синусоиды для анимации волны.
    const wchar_t* text1 = L"Проверка кириллицы"; // Текст первой строки.
    const wchar_t* text2 = L"Latin character check"; // Текст второй строки.
    const wchar_t* text3 = L"1234567890!@#$%^&*()"; // Текст третьей строки.
    const int text1_w = text_width_px(text1); // Фактическая ширина первой строки в пикселях.
    const int text2_w = text_width_px(text2); // Фактическая ширина второй строки в пикселях.
    const int text3_w = text_width_px(text3); // Фактическая ширина третьей строки в пикселях.
    int text1_x = 50; // Начальная X-координата первой строки.
    int text1_y = 90; // Начальная Y-координата первой строки.
    int text2_x = 45; // Начальная X-координата второй строки.
    int text2_y = 110; // Начальная Y-координата второй строки.
    int text3_x = 35; // Начальная X-координата третьей строки.
    int text3_y = 130; // Начальная Y-координата третьей строки.
    int text1_dx = 1; // Горизонтальное направление первой строки: +1 вправо, -1 влево.
    int text1_dy = 1; // Вертикальное направление первой строки: +1 вниз, -1 вверх.
    int text2_dx = -1; // Горизонтальное направление второй строки.
    int text2_dy = 1; // Вертикальное направление второй строки.
    int text3_dx = -1; // Горизонтальное направление третьей строки.
    int text3_dy = -1; // Вертикальное направление третьей строки.
    uint32_t fps_value = 0; // Текущее вычисленное значение FPS.
    uint32_t fps_frame_count = 0; // Количество завершенных кадров в текущем окне измерения.
    uint64_t fps_window_start_us = time_us_64(); // Время старта текущего окна измерения в микросекундах.
    wchar_t fps_text[16] = L"FPS: 0"; // Буфер строки FPS для вывода в угол экрана.

    render_ctx_t rc; // Контекст рендера, который будет привязываться к текущему буферу кадра.

    while (1) // Бесконечный основной цикл приложения.
    { 
        uint16_t* buf = display_begin_paint_try(); // Пытаемся начать новый кадр и получить буфер рисования.
        if (buf == NULL) // Если кадр ещё выводится или paint уже начат, пропускаем этот тик.
        {
            wait_for_irq(); // Спим до прерывания, чтобы не крутить пустой busy-loop.
            continue;
        }
        render_begin(&rc, buf, WIDTH, HEIGHT); // Привязываем контекст рендера к буферу и размерам экрана.

        render_clear(&rc, RGB16(9,19,9)); // Очищаем кадр темно-зеленым фоном.
        render_grid(&rc, 10, 10, 20, RGB16(40,40,40)); // Рисуем фон-сетку с шагом и цветом.
        render_sine_wave(&rc, WIDTH*3, 100, 2.0f, 0, HEIGHT / 2, phase, RGB16(0,255,0)); // Рисуем анимированную синусоиду.
        draw_string(&rc, (uint16_t)text1_x, (uint16_t)text1_y, text1, RGB565(255, 255, 255)); // Рисуем первую строку по текущим X/Y.
        draw_string(&rc, (uint16_t)text2_x, (uint16_t)text2_y, text2, RGB565(0, 0, 255)); // Рисуем вторую строку по текущим X/Y.
        draw_string(&rc, (uint16_t)text3_x, (uint16_t)text3_y, text3, RGB565(255, 0, 0)); // Рисуем третью строку по текущим X/Y.
        draw_string(&rc, 0, 0, fps_text, RGB565(255, 255, 0)); // Выводим текущее значение FPS в левый верхний угол.

        update_axis_reflect(&text1_x, &text1_dx, 0, WIDTH - text1_w); // Двигаем первую строку по X с отражением от левой/правой грани.
        update_axis_reflect(&text1_y, &text1_dy, 0, HEIGHT - TEXT_H); // Двигаем первую строку по Y с отражением от верхней/нижней грани.
        update_axis_reflect(&text2_x, &text2_dx, 0, WIDTH - text2_w); // Двигаем вторую строку по X с отражением от левой/правой грани.
        update_axis_reflect(&text2_y, &text2_dy, 0, HEIGHT - TEXT_H); // Двигаем вторую строку по Y с отражением от верхней/нижней грани.
        update_axis_reflect(&text3_x, &text3_dx, 0, WIDTH - text3_w); // Двигаем третью строку по X с отражением от левой/правой грани.
        update_axis_reflect(&text3_y, &text3_dy, 0, HEIGHT - TEXT_H); // Двигаем третью строку по Y с отражением от верхней/нижней грани.

        phase += 0.08f; // Продвигаем фазу синусоиды для плавной анимации.
        if (phase > 6.2831853f) // Если фаза превысила 2*pi.
        { 
            phase -= 6.2831853f; // Возвращаем фазу в базовый диапазон без скачка формы.
        } 

        bool submitted = display_end_paint(); // Завершаем paint-секцию и отправляем кадр.
        hard_assert(submitted); // В этом сценарии отправка обязана проходить успешно.

        fps_frame_count++; // Учитываем успешно отправленный кадр в статистике FPS.
        uint64_t now_us = time_us_64(); // Фиксируем текущее время для проверки окна измерения.
        uint64_t elapsed_us = now_us - fps_window_start_us; // Сколько прошло с начала окна измерения.
        if (elapsed_us >= 1000000ULL) // Пересчитываем FPS примерно раз в секунду.
        {
            fps_value = (uint32_t)((fps_frame_count * 1000000ULL) / elapsed_us); // Средний FPS за окно.
            (void)swprintf(fps_text, sizeof(fps_text) / sizeof(fps_text[0]), L"FPS: %lu", (unsigned long)fps_value); // Обновляем строку для отрисовки.
            fps_frame_count = 0; // Начинаем новое окно измерения.
            fps_window_start_us = now_us; // Сдвигаем старт окна на текущее время.
        }
    } 
} 
