#include "main.h"
#include "ssd1322.h"
#include <string.h>
#include <stdlib.h>

// Размер буфера: 256x64 пикселей, по 4 бита на пиксель = 256 * 64 / 2 = 8192 байта
uint8_t SSD1322_frameBuffer[SSD1322_WIDTH * SSD1322_HEIGHT / 2] = {0};

static int16_t cursor_x = 0;
static int16_t cursor_y = 0;
static uint8_t text_color = SSD1322_WHITE;

void SSD1322_WriteCommand(uint8_t cmd) {
    SSD1322_COMMAND_ADDR = cmd;
    __NOP(); // Барьер памяти для надежности
}

void SSD1322_WriteData(uint8_t data) {
    SSD1322_DATA_ADDR = data;
    __NOP(); // Барьер памяти для надежности
}

void SSD1322_Reset(void) {
    HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
    HAL_Delay(100);
}

void SSD1322_SetColumnAddress(uint8_t start, uint8_t end) {
    // Адреса колонок: 0-119 (каждая колонка = 4 пикселя)
    if (start > 119) start = 119;
    if (end > 119) end = 119;

    SSD1322_WriteCommand(0x15);
    SSD1322_WriteData(start);
    SSD1322_WriteData(end);
}

void SSD1322_SetRowAddress(uint8_t start, uint8_t end) {
    if (start > 127) start = 127;
    if (end > 127) end = 127;

    SSD1322_WriteCommand(0x75);
    SSD1322_WriteData(start);
    SSD1322_WriteData(end);
}

void SSD1322_SetWindow(uint8_t col_start, uint8_t col_end,
                       uint8_t row_start, uint8_t row_end) {
    SSD1322_SetColumnAddress(col_start, col_end);
    SSD1322_SetRowAddress(row_start, row_end);
}

// Вспомогательная функция для отладки (опционально)
static void SSD1322_DebugPrint(const char* msg) {
    // Если у вас есть UART для отладки
    // HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
}

void SSD1322_Init(void) {
    // 1. Аппаратный сброс
    SSD1322_DebugPrint("SSD1322: Resetting...\r\n");
    SSD1322_Reset();

    // 2. Выключить дисплей
    SSD1322_WriteCommand(0xAE); // Display OFF

    // 3. Разблокировать команды
    SSD1322_WriteCommand(0xFD); // Command Lock
    SSD1322_WriteData(0x12);    // Unlock

    // 4. Установить MUX ratio = 64 (наш дисплей 64 строки)
    SSD1322_WriteCommand(0xCA); // Set MUX Ratio
    SSD1322_WriteData(0x3F);    // 64 MUX (0x3F = 63, +1 = 64)

    // 5. Установить смещение дисплея (Display Offset) для центрирования
    SSD1322_WriteCommand(0xA2); // Set Display Offset
    SSD1322_WriteData(0x20);    // 32 строки смещения для 64-строчного дисплея

    // 6. Установить стартовую линию
    SSD1322_WriteCommand(0xA1); // Set Display Start Line
    SSD1322_WriteData(0x00);    // Start line = 0

    // 7. ⚡ ВАЖНО: Настройка ремапа (Remap) для 256x64 ⚡
    SSD1322_WriteCommand(0xA0); // Set Re-map & Dual COM Line Mode
    // Байт A:
    // Bit0=0: Horizontal address increment (горизонтальный инкремент)
    // Bit1=1: Enable column address remap (включить ремап колонок)
    // Bit2=0: Disable nibble remap (без ремапа нибблов)
    // Bit4=0: Scan from COM0 to COM[N-1]
    // Bit5=0: Disable COM split odd even (для Dual COM mode)
    // 0b01010000 = 0x50
    SSD1322_WriteData(0x50);

    // Байт B: Включить Dual COM mode для 64 строк
    // Bit4=1: Enable Dual COM mode (MUX ≤ 63)
    // 0b00010001 = 0x11
    SSD1322_WriteData(0x11);

    // 8. Установить контраст
    SSD1322_SetContrast(0xCF);  // Оптимальный контраст

    // 9. Установить напряжение предзаряда
    SSD1322_WriteCommand(0xBB); // Set Pre-charge voltage
    SSD1322_WriteData(0x1F);    // 0.6*VCC (максимум)

    // 10. Установить VCOMH
    SSD1322_WriteCommand(0xBE); // Set VCOMH Voltage
    SSD1322_WriteData(0x07);    // 0.86*VCC (максимум)

    // 11. Настроить фазы
    SSD1322_WriteCommand(0xB1); // Set Phase Length
    SSD1322_WriteData(0xE2);    // Phase 1: 9 DCLKs, Phase 2: 7 DCLKs

    // 12. Настроить делитель частоты и осциллятор
    SSD1322_WriteCommand(0xB3); // Set Clock Divider / Oscillator Frequency
    // A[7:4] = 1001b (9) - частота осциллятора
    // A[3:0] = 0010b (2) - делитель = 4
    // 0b10010010 = 0x92 (частота кадров ~85 Гц)
    SSD1322_WriteData(0x92);

    // 13. Включить внутренний регулятор VDD
    SSD1322_WriteCommand(0xAB); // Function Selection
    SSD1322_WriteData(0x01);    // Enable internal VDD regulator

    // 14. Установить второй предзаряд
    SSD1322_WriteCommand(0xB6); // Set Second Pre-charge Period
    SSD1322_WriteData(0x08);    // 8 DCLKs

    // 15. Выбрать линейную гамма-таблицу
    SSD1322_WriteCommand(0xB9); // Select Default Linear Gray Scale Table

    // 16. Нормальный режим дисплея (не инверсный)
    SSD1322_WriteCommand(0xA6); // Normal display

    // 17. ⚡ Установить окно отображения для нашей области 256x64 ⚡
    // 256 пикселей = 64 колонки (256/4)
    // Начинаем с колонки 112 для центрирования в 480 сегментах: (480-256)/2/4 = 28
    // Но по документации SSD1322: 256 пикселей = 64 колонки, начинаем с 0
    SSD1322_SetWindow(0, 63, 0, 63);

    // 18. Очистить экран
    SSD1322_Clear();
    SSD1322_Update();

    // 19. Включить дисплей
    SSD1322_WriteCommand(0xAF); // Display ON

    HAL_Delay(100);
    SSD1322_DebugPrint("SSD1322: Initialized successfully!\r\n");
}

void SSD1322_SetContrast(uint8_t contrast) {
    SSD1322_WriteCommand(0xC1);
    SSD1322_WriteData(contrast);
}

void SSD1322_DisplayOn(void) {
    SSD1322_WriteCommand(0xAF);
}

void SSD1322_DisplayOff(void) {
    SSD1322_WriteCommand(0xAE);
}

void SSD1322_Clear(void) {
    memset(SSD1322_frameBuffer, 0, sizeof(SSD1322_frameBuffer));
}

void SSD1322_Update(void) {
    SSD1322_UpdateArea(0, SSD1322_WIDTH-1, 0, SSD1322_HEIGHT-1);
}

void SSD1322_UpdateArea(uint8_t x_start, uint8_t x_end,
                        uint8_t y_start, uint8_t y_end) {
    // Преобразуем пиксельные координаты в координаты SSD1322
    // Каждая колонка SSD1322 = 4 пикселя, строки 1:1

    uint8_t col_start = SSD1322_COL_START + (x_start / 4);
    uint8_t col_end = SSD1322_COL_START + (x_end / 4);
    uint8_t row_start = SSD1322_ROW_START + y_start;
    uint8_t row_end = SSD1322_ROW_START + y_end;

    // Устанавливаем окно
    SSD1322_SetWindow(col_start, col_end, row_start, row_end);

    SSD1322_WriteCommand(0x5C); // Write RAM Command

    // Рассчитываем область в буфере

    uint16_t width_in_bytes = (x_end - x_start + 1) / 2;
    if ((x_end - x_start + 1) % 2) width_in_bytes++; // если нечётное количество пикселей

    for(uint8_t row = y_start; row <= y_end; row++) {
        uint16_t row_start_idx = row * (SSD1322_WIDTH / 2) + (x_start / 2);
        for(uint16_t i = 0; i < width_in_bytes; i++) {
            SSD1322_WriteData(SSD1322_frameBuffer[row_start_idx + i]);
        }
    }
}

void SSD1322_DrawPixel(int16_t x, int16_t y, uint8_t color) {
    if(x < 0 || x >= SSD1322_WIDTH || y < 0 || y >= SSD1322_HEIGHT)
        return;

    color &= 0x0F; // Ограничиваем 4 битами

    uint32_t index = (y * (SSD1322_WIDTH / 2)) + (x / 2);

    if(x & 1) {
        // Нечетный x: младшие 4 бита
        SSD1322_frameBuffer[index] = (SSD1322_frameBuffer[index] & 0xF0) | color;
    } else {
        // Четный x: старшие 4 бита
        SSD1322_frameBuffer[index] = (SSD1322_frameBuffer[index] & 0x0F) | (color << 4);
    }
}

void SSD1322_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color) {
    int16_t dx = abs(x1 - x0);
    int16_t dy = abs(y1 - y0);
    int16_t sx = (x0 < x1) ? 1 : -1;
    int16_t sy = (y0 < y1) ? 1 : -1;
    int16_t err = dx - dy;

    while(1) {
        SSD1322_DrawPixel(x0, y0, color);
        if(x0 == x1 && y0 == y1) break;
        int16_t e2 = err * 2;
        if(e2 > -dy) {
            err -= dy;
            x0 += sx;
        }
        if(e2 < dx) {
            err += dx;
            y0 += sy;
        }
    }
}

void SSD1322_DrawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color) {
    SSD1322_DrawLine(x, y, x + w - 1, y, color);          // Верхняя
    SSD1322_DrawLine(x, y + h - 1, x + w - 1, y + h - 1, color); // Нижняя
    SSD1322_DrawLine(x, y, x, y + h - 1, color);          // Левая
    SSD1322_DrawLine(x + w - 1, y, x + w - 1, y + h - 1, color); // Правая
}

void SSD1322_DrawFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color) {
    for(int16_t i = x; i < x + w; i++) {
        for(int16_t j = y; j < y + h; j++) {
            SSD1322_DrawPixel(i, j, color);
        }
    }
}

void SSD1322_DrawCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color) {
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    SSD1322_DrawPixel(x0, y0 + r, color);
    SSD1322_DrawPixel(x0, y0 - r, color);
    SSD1322_DrawPixel(x0 + r, y0, color);
    SSD1322_DrawPixel(x0 - r, y0, color);

    while(x < y) {
        if(f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        SSD1322_DrawPixel(x0 + x, y0 + y, color);
        SSD1322_DrawPixel(x0 - x, y0 + y, color);
        SSD1322_DrawPixel(x0 + x, y0 - y, color);
        SSD1322_DrawPixel(x0 - x, y0 - y, color);
        SSD1322_DrawPixel(x0 + y, y0 + x, color);
        SSD1322_DrawPixel(x0 - y, y0 + x, color);
        SSD1322_DrawPixel(x0 + y, y0 - x, color);
        SSD1322_DrawPixel(x0 - y, y0 - x, color);
    }
}

void SSD1322_DrawFillCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color) {
    SSD1322_DrawLine(x0 - r, y0, x0 + r, y0, color);

    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    while(x < y) {
        if(f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        SSD1322_DrawLine(x0 - x, y0 + y, x0 + x, y0 + y, color);
        SSD1322_DrawLine(x0 - x, y0 - y, x0 + x, y0 - y, color);
        SSD1322_DrawLine(x0 - y, y0 + x, x0 + y, y0 + x, color);
        SSD1322_DrawLine(x0 - y, y0 - x, x0 + y, y0 - x, color);
    }
}



// ===================== РАБОТА С ТЕКСТОМ =====================
// Базовый шрифт 8x8 (упрощенный)
static const uint8_t font8x8_basic[96][8] = {
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // space
    {0x18,0x3C,0x3C,0x18,0x18,0x00,0x18,0x00}, // !
    {0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00}, // "
    {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, // #
    {0x0C,0x3E,0x03,0x1E,0x30,0x1F,0x0C,0x00}, // $
    {0x63,0x33,0x18,0x0C,0x06,0x33,0x31,0x00}, // %
    {0x1C,0x36,0x1C,0x6E,0x3B,0x33,0x6E,0x00}, // &
    {0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00}, // '
    {0x18,0x0C,0x06,0x06,0x06,0x0C,0x18,0x00}, // (
    {0x06,0x0C,0x18,0x18,0x18,0x0C,0x06,0x00}, // )
    {0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00}, // *
    {0x00,0x0C,0x0C,0x3F,0x0C,0x0C,0x00,0x00}, // +
    {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x06}, // ,
    {0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00}, // -
    {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x00}, // .
    {0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00}, // /
    {0x3E,0x63,0x73,0x7B,0x6F,0x67,0x3E,0x00}, // 0
    {0x0C,0x0E,0x0C,0x0C,0x0C,0x0C,0x3F,0x00}, // 1
    {0x1E,0x33,0x30,0x1C,0x06,0x33,0x3F,0x00}, // 2
    {0x1E,0x33,0x30,0x1C,0x30,0x33,0x1E,0x00}, // 3
    {0x38,0x3C,0x36,0x33,0x7F,0x30,0x78,0x00}, // 4
    {0x3F,0x03,0x1F,0x30,0x30,0x33,0x1E,0x00}, // 5
    {0x1C,0x06,0x03,0x1F,0x33,0x33,0x1E,0x00}, // 6
    {0x3F,0x33,0x30,0x18,0x0C,0x0C,0x0C,0x00}, // 7
    {0x1E,0x33,0x33,0x1E,0x33,0x33,0x1E,0x00}, // 8
    {0x1E,0x33,0x33,0x3E,0x30,0x18,0x0E,0x00}, // 9
    {0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x00}, // :
    {0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x06}, // ;
    {0x18,0x0C,0x06,0x03,0x06,0x0C,0x18,0x00}, // <
    {0x00,0x00,0x3F,0x00,0x00,0x3F,0x00,0x00}, // =
    {0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00}, // >
    {0x1E,0x33,0x30,0x18,0x0C,0x00,0x0C,0x00}, // ?
    {0x3E,0x63,0x7B,0x7B,0x7B,0x03,0x1E,0x00}, // @
    {0x0C,0x1E,0x33,0x33,0x3F,0x33,0x33,0x00}, // A
    {0x3F,0x66,0x66,0x3E,0x66,0x66,0x3F,0x00}, // B
    {0x3C,0x66,0x03,0x03,0x03,0x66,0x3C,0x00}, // C
    {0x1F,0x36,0x66,0x66,0x66,0x36,0x1F,0x00}, // D
    {0x7F,0x46,0x16,0x1E,0x16,0x46,0x7F,0x00}, // E
    {0x7F,0x46,0x16,0x1E,0x16,0x06,0x0F,0x00}, // F
    {0x3C,0x66,0x03,0x03,0x73,0x66,0x7C,0x00}, // G
    {0x33,0x33,0x33,0x3F,0x33,0x33,0x33,0x00}, // H
    {0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00}, // I
    {0x78,0x30,0x30,0x30,0x33,0x33,0x1E,0x00}, // J
    {0x67,0x66,0x36,0x1E,0x36,0x66,0x67,0x00}, // K
    {0x0F,0x06,0x06,0x06,0x46,0x66,0x7F,0x00}, // L
    {0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00}, // M
    {0x63,0x67,0x6F,0x7B,0x73,0x63,0x63,0x00}, // N
    {0x1C,0x36,0x63,0x63,0x63,0x36,0x1C,0x00}, // O
    {0x3F,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00}, // P
    {0x1E,0x33,0x33,0x33,0x3B,0x1E,0x38,0x00}, // Q
    {0x3F,0x66,0x66,0x3E,0x36,0x66,0x67,0x00}, // R
    {0x1E,0x33,0x07,0x0E,0x38,0x33,0x1E,0x00}, // S
    {0x3F,0x2D,0x0C,0x0C,0x0C,0x0C,0x1E,0x00}, // T
    {0x33,0x33,0x33,0x33,0x33,0x33,0x3F,0x00}, // U
    {0x33,0x33,0x33,0x33,0x33,0x1E,0x0C,0x00}, // V
    {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}, // W
    {0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00}, // X
    {0x33,0x33,0x33,0x1E,0x0C,0x0C,0x1E,0x00}, // Y
    {0x7F,0x63,0x31,0x18,0x4C,0x66,0x7F,0x00}, // Z
    {0x1E,0x06,0x06,0x06,0x06,0x06,0x1E,0x00}, // [
    {0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00}, //
    {0x1E,0x18,0x18,0x18,0x18,0x18,0x1E,0x00}, // ]
    {0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00}, // ^
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, // _
    {0x0C,0x0C,0x18,0x00,0x00,0x00,0x00,0x00}, // `
    {0x00,0x00,0x1E,0x30,0x3E,0x33,0x6E,0x00}, // a
    {0x07,0x06,0x06,0x3E,0x66,0x66,0x3B,0x00}, // b
    {0x00,0x00,0x1E,0x33,0x03,0x33,0x1E,0x00}, // c
    {0x38,0x30,0x30,0x3e,0x33,0x33,0x6E,0x00}, // d
    {0x00,0x00,0x1E,0x33,0x3f,0x03,0x1E,0x00}, // e
    {0x1C,0x36,0x06,0x0f,0x06,0x06,0x0F,0x00}, // f
    {0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x1F}, // g
    {0x07,0x06,0x36,0x6E,0x66,0x66,0x67,0x00}, // h
    {0x0C,0x00,0x0E,0x0C,0x0C,0x0C,0x1E,0x00}, // i
    {0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1E}, // j
    {0x07,0x06,0x66,0x36,0x1E,0x36,0x67,0x00}, // k
    {0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00}, // l
    {0x00,0x00,0x33,0x7F,0x7F,0x6B,0x63,0x00}, // m
    {0x00,0x00,0x1F,0x33,0x33,0x33,0x33,0x00}, // n
    {0x00,0x00,0x1E,0x33,0x33,0x33,0x1E,0x00}, // o
    {0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F}, // p
    {0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78}, // q
    {0x00,0x00,0x3B,0x6E,0x66,0x06,0x0F,0x00}, // r
    {0x00,0x00,0x3E,0x03,0x1E,0x30,0x1F,0x00}, // s
    {0x08,0x0C,0x3E,0x0C,0x0C,0x2C,0x18,0x00}, // t
    {0x00,0x00,0x33,0x33,0x33,0x33,0x6E,0x00}, // u
    {0x00,0x00,0x33,0x33,0x33,0x1E,0x0C,0x00}, // v
    {0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00}, // w
    {0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00}, // x
    {0x00,0x00,0x33,0x33,0x33,0x3E,0x30,0x1F}, // y
    {0x00,0x00,0x3F,0x19,0x0C,0x26,0x3F,0x00}, // z
    {0x38,0x0C,0x0C,0x07,0x0C,0x0C,0x38,0x00}, // {
    {0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00}, // |
    {0x07,0x0C,0x0C,0x38,0x0C,0x0C,0x07,0x00}, // }
    {0x6E,0x3B,0x00,0x00,0x00,0x00,0x00,0x00}, // ~
};

void SSD1322_SetCursor(int16_t x, int16_t y) {
    cursor_x = x;
    cursor_y = y;
}

void SSD1322_SetTextColor(uint8_t color) {
    text_color = color;
}

void SSD1322_WriteChar(char ch) {
    if(ch < 32 || ch > 127) ch = '?';

    const uint8_t* font = font8x8_basic[ch - 32];

    for(uint8_t row = 0; row < 8; row++) {
        uint8_t data = font[row];
        for(uint8_t col = 0; col < 8; col++) {
            if(data & (1 << (7 - col))) {
                SSD1322_DrawPixel(cursor_x + col, cursor_y + row, text_color);
            }
        }
    }

    cursor_x += 8;
    if(cursor_x >= SSD1322_WIDTH - 8) {
        cursor_x = 0;
        cursor_y += 8;
    }
}

void SSD1322_WriteString(const char* str) {
    while(*str) {
        SSD1322_WriteChar(*str++);
    }
}

// Новые утилиты
uint16_t SSD1322_GetWidth(void) {
    return SSD1322_WIDTH;
}

uint8_t SSD1322_GetHeight(void) {
    return SSD1322_HEIGHT;
}

void SSD1322_WriteStringCentered(uint8_t y, const char* str) {
    int16_t len = strlen(str) * 8;
    int16_t x = (SSD1322_WIDTH - len) / 2;
    if(x < 0) x = 0;

    SSD1322_SetCursor(x, y);
    SSD1322_WriteString(str);
}

void SSD1322_WriteStringAt(uint8_t x, uint8_t y, const char* str) {
    SSD1322_SetCursor(x, y);
    SSD1322_WriteString(str);
}
