//#######################################
//⑤ GMT130表示関係関数 (gmt130_lcd.c 想定)
//#######################################
//※ST7735ドライバや描画関数群は既存のライブラリやサンプルを流用する前提の擬似コードです。
#include "main.h"
#include "gmt130_lcd.h"
#include <string.h>
#include "font.h"

// フォントデータ 外部参照
extern const unsigned char ascii_8x16[];

//　I/Oの設定
void LCD_IO(void) {

}


// SPI1経由での1バイト送信
static void SPI1_WriteByte(uint8_t data) {
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, data);
}
//LCD_CS はGMT130については物理的に無いので不要です

// コマンド送信
static void LCD_WriteCommand(uint8_t cmd) {
    LCD_DC_CMD();
//    LCD_CS_LOW();
    SPI1_WriteByte(cmd);
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
//    LCD_CS_HIGH();
}

// 1バイトデータ送信
static void LCD_WriteData8(uint8_t data) {
    LCD_DC_DATA();
//    LCD_CS_LOW();
    SPI1_WriteByte(data);
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
//    LCD_CS_HIGH();
}

/* // 2バイトデータ（カラー等）送信 不使用のためコメントアウト
static void LCD_WriteData16(uint16_t data) {
    LCD_DC_DATA();
//    LCD_CS_LOW();
    SPI1_WriteByte(data >> 8);
    SPI1_WriteByte(data & 0xFF);
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
//    LCD_CS_HIGH();
} */

// 初期化関数
void LCD_Init(void) {
    // 1.I/Oの設定
    LCD_IO();

    // 2. リセットシーケンス
 //   「ST7789のICの電源ON時の初期値（デフォルト値）」と「240x240の物理液晶が、IC内部の240x320メモリ

    LCD_RST_LOW();
    Delay_Ms(20);
    LCD_RST_HIGH();
    Delay_Ms(50);

    // 3. ST7735 コマンド初期化
    LCD_WriteCommand(0x11); // Sleep out
    Delay_Ms(120);

    LCD_WriteCommand(0x12); // Normal Display Mode ON (Partial Modeを無効化)
    Delay_Ms(10);        // 念のため少し待つ
 //   LCD_WriteCommand(0x33); // Vertical Scrolling Definition (33h)
 //   LCD_WriteData8(0x00); LCD_WriteData8(0x00); // Top Fixed Area = 0行
 //   LCD_WriteData8(0x01); LCD_WriteData8(0x40); // Vertical Scrolling Area = 320行 (全域)
 //   LCD_WriteData8(0x00); LCD_WriteData8(0x00); // Bottom Fixed Area = 0行

    LCD_WriteCommand(0x21); // 表示反転をオンにする

    LCD_SetRotation(2);  //0~3

    LCD_WriteCommand(0x3A); // Interface pixel format (16-bit color)
    LCD_WriteData8(0x05);

    LCD_WriteCommand(0x29); // Display on
    Delay_Ms(10);

    LCD_Clear(BLACK);
}

// 描画エリアの設定
static void LCD_SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
    // 240x240 ST7735S のオフセット調整（パネル個体により必要に応じて+2等調整）
    uint8_t offset_x = 2; 
    uint8_t offset_y = 3;

    LCD_WriteCommand(0x2A); // Column Address Set
    LCD_WriteData8(0x00); LCD_WriteData8(x0 + offset_x);
    LCD_WriteData8(0x00); LCD_WriteData8(x1 + offset_x);

    LCD_WriteCommand(0x2B); // Row Address Set
    LCD_WriteData8(0x00); LCD_WriteData8(y0 + offset_y);
    LCD_WriteData8(0x00); LCD_WriteData8(y1 + offset_y);

    LCD_WriteCommand(0x2C); // Memory Write
}

// 画面の向き（ローテーション）を設定する関数
/* コマンド 36h　のデータ
• D7 (MY): 行アドレスの方向（0: 上から下 / 1: 下から上）
• D6 (MX): 列アドレスの方向（0: 左から右 / 1: 右から左）
• D5 (MV): 行と列の入れ替え（0: 縦モード / 1: 横モード）
• D4 (ML): 垂直スキャン方向（通常は 0）
• D3 (RGB): カラー配置（0: RGB / 1: BGR）
• D2 (MH): 水平スキャン方向（通常は 0）
• D1, D0: 未使用（0 固定)*/
// mode: 0 = 標準(縦), 1 = 90度右回転(横), 2 = 180度回転(逆縦), 3 = 270度右回転(逆横)
void LCD_SetRotation(uint8_t mode) {
    uint8_t scroll_start = 0;
    uint8_t madctl_param = 0;

    switch (mode) {
        case 0:
            // 標準 (縦画面)
            madctl_param = 0x00; // パネルによって 0xC8 や 0x08 など調整
            break;
        case 1:
            // 90度右回転 (横画面)
            madctl_param = 0x60; // パネルによって 0xA8 や 0x68 など調整
            scroll_start = 0;            
            break;
        case 2:
            // 180度回転 (逆さ縦画面)
            madctl_param = 0xC0; // パネルによって 0x48 や 0xC8 など調整
            scroll_start = 80;
            break;
        case 3:
            // 270度右回転 (逆さ横画面)
            madctl_param = 0xA0; // パネルによって 0x18 や 0xA8 など調整
            scroll_start = 80;  
            break;
        default:
            return;
    }

    LCD_WriteCommand(0x36); // MADCTL コマンド
    LCD_WriteData8(madctl_param);

    LCD_WriteCommand(0x37); // Vertical Scrolling Start Address (37h)
    LCD_WriteData8(0x00); LCD_WriteData8(scroll_start); // スクロール開始位置を 0 にリセット
}
/*クスロールの設定について
• ST7796Sは「画素」と「内部メモリ」のサイズが完全に一致していたため、回転時もズレなかった。
• ST7789(GMT130)は「画素(240x240)」に対し「内部メモリ(240x320)」が縦に80ドット分大きかった。
• 画面を180度回転(0xC0)させると、ICはメモリの最下部(319行目)から逆順にスキャンを開始する。
• デフォルト状態のままだと、物理液晶の枠外にある「余った80ドット分の領域」が先頭に回り込んでしまう。
• これにより、画面上部の80ドットに過去のデータが固定され、表示が真っ二つに分断される現象が起きた。
• 対策として、スクロール開始位置を指定する37h (Vertical Scrolling Start Address) コマンドを使用した。
• 180度および270度回転時に37hの値を「80」に設定し、メモリの開始基準点を80ドット分シフトさせた。
• この制御により、液晶パネルの物理的な配線位置とICのメモリ走査のスタート位置が完璧に同期した。
• 結果、描画関数側の座標計算(2Ah/2Bh)を一切変更することなく、全画面を綺麗に回転できた。
• 教訓：「画素数＜メモリ数」の液晶を反転させる際は、36hだけでなく37hの基準点シフトもセットで行う。  */


// 輝度（バックライトPWM）制御
void LCD_SetBrightness(uint8_t percent) {
    if(percent > 100) percent = 100;
    TIM_SetCompare1(TIM1, percent);
}

// 画面全体のクリア
void LCD_Clear(uint16_t color) {
    LCD_DrawFillRect(0, 0, 240,240, color);
}

// 指定エリアの塗りつぶし
void LCD_DrawFillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
    if((x >= 240) || (y >= 240)) return;
    if((x + w) > 240) w = 240 - x;
    if((y + h) > 240) h = 240 - y;

    LCD_SetWindow(x, y, x + w - 1, y + h - 1);
    LCD_DC_DATA();
//    LCD_CS_LOW();
    for(uint16_t i = 0; i < (w * h); i++) {
        SPI1_WriteByte(color >> 8);
        SPI1_WriteByte(color & 0xFF);
    }
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
//    LCD_CS_HIGH();
}

// 1文字描画 (ascii_8x16フォント用)
void LCD_DrawChar(uint8_t x, uint8_t y, char c, uint16_t color, uint16_t bg) {
    if(c < ' ' || c > '~') c = ' '; // 範囲外はスペース
    uint16_t font_idx = (c - ' ') * 16; // 1文字あたり16バイト配列のオフセット

    LCD_SetWindow(x, y, x + 8 - 1, y + 16 - 1);
    LCD_DC_DATA();
//    LCD_CS_LOW();

    // 縦16行ループ
    for(uint8_t row = 0; row < 16; row++) {
        uint8_t b = ascii_8x16[font_idx + row];
        // 横8ビットループ (MSBファースト)
        for(uint8_t col = 0; col < 8; col++) {
            uint16_t pixel_color = (b & (0x80 >> col)) ? color : bg;
            SPI1_WriteByte(pixel_color >> 8);
            SPI1_WriteByte(pixel_color & 0xFF);
        }
    }
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
//    LCD_CS_HIGH();
}

// 文字列描画
void LCD_DrawString(uint8_t x, uint8_t y, const char* str, uint16_t color, uint16_t bg) {
    while(*str) {
        if(x + 8 > 240) {
            x = 0;
            y += 16;
        }
        if(y + 16 > 240) break;
        LCD_DrawChar(x, y, *str, color, bg);
        x += 8;
        str++;
    }
}

// バーグラフ描画 [■■■□]
void LCD_DrawBar(uint8_t x, uint8_t y, uint8_t val, uint8_t max_val) {
    LCD_DrawChar(x,      y, '[', WHITE, BLACK);
    for(uint8_t i = 0; i < max_val; i++) {
        if(i < val) {
            // 塗りつぶし文字、またはフォント上の四角(■)に対応するアスキーコード
            // 通常フォントに無い場合は■を自作するか、DrawCharの代わりにDrawFillRectでバーを描画
            // ここでは簡易的にフォント依存を避けDrawFillRectで四角を描画します
            LCD_DrawFillRect(x + 8 + (i * 8) + 1, y + 2, 6, 12, GREEN);
        } else {
            LCD_DrawFillRect(x + 8 + (i * 8) + 1, y + 2, 6, 12, GRAY);
        }
    }
    LCD_DrawChar(x + 8 + (max_val * 8), y, ']', WHITE, BLACK);
}

void LCD_Display_Update(SystemParams* params) {
    char buf[32];

    // バックライト輝度更新
    LCD_SetBrightness(params->gnt);

    // タイトル
    LCD_DrawString(10, 5, "[ BPF FILTER SETTING ]", WHITE, BLACK);

    // F0 表示 (500~1500Hz)
    sprintf(buf, "F0 : %4d Hz", params->f0);
    LCD_DrawString(5, 25, buf, WHITE, BLACK);
    uint8_t f0_bar = (params->f0 - 500) / 250; // 0〜4の5段階
    LCD_DrawBar(110, 25, f0_bar, 4);

    // BW 表示 (±10~±1500Hzを2倍表示 ＝ 20~3000Hz表示)
    sprintf(buf, "BW : %4d Hz", params->bw * 2);
    LCD_DrawString(5, 42, buf, WHITE, BLACK);
    uint8_t bw_bar = (params->bw - 10) / 375;  // 0〜4の5段階
    LCD_DrawBar(110, 42, bw_bar, 4);

    // ATT 表示
    sprintf(buf, "ATT:  %d次 (-%ddB)", params->att_order, (params->att_order) * 6);
    LCD_DrawString(5, 59, buf, WHITE, BLACK);

    // SGC 表示
    sprintf(buf, "SGC: %4d mV", params->sgc);
    LCD_DrawString(5, 76, buf, WHITE, BLACK);

    // GNT 表示
    sprintf(buf, "GNT:  %3d %%", params->gnt);
    LCD_DrawString(5, 93, buf, WHITE, BLACK);

    // SGS 表示 (センター電圧)
    sprintf(buf, "SGS: %4d mV", params->sgs);
    LCD_DrawString(5, 110, buf, WHITE, BLACK);

    // NzV 表示 (ノイズ電圧)
    sprintf(buf, "NzV: %4d mV", params->nzv);
    LCD_DrawString(5, 127, buf, WHITE, BLACK); // 240x240画面最下部まで活用
}


