#include "../2025_include_user/OledMenuDriver.h"

#define OLED_I2C_ADDR (0x3C)  // 7-bit地址，不需要 <<1
#define SYSTEM_CORE_CLOCK_HZ 80000000U

u8g2_t MenuDriver::u8g2 = {};

uint8_t u8x8_gpio_and_delay_mspm0(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_GPIO_AND_DELAY_INIT:
            delay_cycles(32000); //1ms
            break;
        case U8X8_MSG_DELAY_MILLI:
            for (uint8_t i = 0; i < arg_int; i++) {
                delay_cycles(32000);
            }

            break;
        case U8X8_MSG_GPIO_DC:
            break;
        case U8X8_MSG_GPIO_RESET: /**
        if (arg_int)
        {
              DL_GPIO_setPins(GPIO_OLED_PORT, GPIO_OLED_RST_OLED_PIN);
		}
        else
        {
            DL_GPIO_clearPins(GPIO_OLED_PORT, GPIO_OLED_RST_OLED_PIN);
		}**/
            break;
    }
    return 1;
}


uint8_t u8x8_byte_mspm0_hw_i2c(u8x8_t *u8g2, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
    /* add extra byte for the i2c address */
    static uint8_t buffer[34];
    static volatile uint8_t buf_idx, g_u8TxCount;
    uint8_t *data;
    switch (msg) {
        case U8X8_MSG_BYTE_SEND:
            data = (uint8_t *) arg_ptr;
            while (arg_int > 0) {
                buffer[buf_idx++] = *data;
                data++;
                arg_int--;
            }
            break;

        case U8X8_MSG_BYTE_INIT:
            /* add your custom code to init i2c subsystem */

            break;
        case U8X8_MSG_BYTE_SET_DC:
            /* ignored for i2c */
            break;
        case U8X8_MSG_BYTE_START_TRANSFER:
            buf_idx = 0;
            break;
        case U8X8_MSG_BYTE_END_TRANSFER:
            /*
            * Fill FIFO with data.
            */
            g_u8TxCount = DL_I2C_fillControllerTXFIFO(I2C_0_INST, &buffer[0], buf_idx);

        /* Wait for I2C to be Idle */
            while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

        /* Send the packet to the controller.
        * This function will send Start + Stop automatically.
        */
            DL_I2C_startControllerTransfer(I2C_0_INST, OLED_I2C_ADDR,
                                           DL_I2C_CONTROLLER_DIRECTION_TX, buf_idx);

            while (g_u8TxCount < buf_idx) {
                while (DL_I2C_isControllerTXFIFOFull(I2C_0_INST) == true);
                DL_I2C_transmitControllerData(I2C_0_INST, buffer[g_u8TxCount]);
                g_u8TxCount++;
            }

        /* Poll until the Controller writes all bytes */
            while (DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);

        /* Trap if there was an error */
            if (DL_I2C_getControllerStatus(I2C_0_INST) &
                DL_I2C_CONTROLLER_STATUS_ERROR) {
                while (1);
            }

        /* Wait for I2C to be Idle */
            while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

            DL_I2C_flushControllerTXFIFO(I2C_0_INST);

            break;

        default:
            return 0;
    }
    return 1;
}

void MenuDriver::oled_init() {
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_mspm0_hw_i2c, u8x8_gpio_and_delay_mspm0);
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
    u8g2_ClearBuffer(&u8g2);
}

void MenuDriver::ssd1306_transmit_cmd(uint8_t cmd) {
    uint8_t temp[2] = {0x00, cmd};

    DL_I2C_fillControllerTXFIFO(I2C_0_INST, temp, 2);
    while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

    DL_I2C_startControllerTransfer(I2C_0_INST, OLED_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_TX, 2);
    while (DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
    while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

    DL_I2C_flushControllerTXFIFO(I2C_0_INST);
}

void MenuDriver::ssd1306_transmit_data(uint8_t data, const uint8_t mode) {
    if (!mode)
        data = ~data;
    uint8_t temp[2] = {0x40, data};

    DL_I2C_fillControllerTXFIFO(I2C_0_INST, temp, 2);
    while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

    DL_I2C_startControllerTransfer(I2C_0_INST, OLED_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_TX, 2);
    while (DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
    while (!(DL_I2C_getControllerStatus(I2C_0_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));

    DL_I2C_flushControllerTXFIFO(I2C_0_INST);
}

void MenuDriver::ssd1306_set_cursor(unsigned char _x, unsigned char _y) {
    ssd1306_transmit_cmd(0xB0 | _y);
    ssd1306_transmit_cmd(0x10 | ((_x & 0xF0) >> 4));
    ssd1306_transmit_cmd(0x00 | (_x & 0x0F));
}

void MenuDriver::ssd1306_fill(unsigned char _data) {
    for (uint8_t page = 0; page < 8; ++page) {
        ssd1306_set_cursor(0, page);
        for (uint8_t col = 0; col < 128; ++col) {
            ssd1306_transmit_data(_data, 1);
        }
    }
}

void MenuDriver::screenOn() {
    ssd1306_transmit_cmd(0X8D);
    ssd1306_transmit_cmd(0X14);
    ssd1306_transmit_cmd(0XAF);
}

void MenuDriver::screenOff() {
    ssd1306_transmit_cmd(0X8D);
    ssd1306_transmit_cmd(0X10);
    ssd1306_transmit_cmd(0XAE);
}

void *MenuDriver::getCanvasBuffer() {
    return u8g2_GetBufferPtr(&u8g2);
}

uint8_t MenuDriver::getBufferTileHeight() {
    return u8g2_GetBufferTileHeight(&u8g2);
}

uint8_t MenuDriver::getBufferTileWidth() {
    return u8g2_GetBufferTileWidth(&u8g2);
}

void MenuDriver::canvasUpdate() {
    u8g2_SendBuffer(&u8g2);
}

void MenuDriver::canvasClear() {
    u8g2_ClearBuffer(&u8g2);
}

void MenuDriver::setFont(const uint8_t *_font) {
    u8g2_SetFontMode(&u8g2, 1);
    u8g2_SetFontDirection(&u8g2, 0);
    u8g2_SetFont(&u8g2, _font);
}

void MenuDriver::setFont(const uint8_t *_font, uint8_t _font_mode, uint8_t _font_direction) {
    u8g2_SetFontMode(&u8g2, _font_mode);
    u8g2_SetFontDirection(&u8g2, _font_direction);
    u8g2_SetFont(&u8g2, _font);
}

uint8_t MenuDriver::getFontWidth(const char *_text) {
    return u8g2_GetUTF8Width(&u8g2, _text);
}

uint8_t MenuDriver::getFontHeight() {
    return u8g2_GetMaxCharHeight(&u8g2);
}

void MenuDriver::setDrawType(uint8_t _type) {
    u8g2_SetDrawColor(&u8g2, _type);
}

void MenuDriver::drawPixel(float _x, float _y) {
    u8g2_DrawPixel(&u8g2, (int16_t) round(_x), (int16_t) round(_y));
}

void MenuDriver::draw(float _x, float _y, const char *_text) {
    setFont(u8g2_font_wqy14_t_chinese3);
    u8g2_DrawUTF8(&u8g2, (int16_t) round(_x), (int16_t) round(_y), _text);
}

void MenuDriver::draw(float _x, float _y, const char *_text, uint8_t _font_mode, uint8_t _font_direction) {
    setFont(u8g2_font_wqy14_t_chinese3, _font_mode, _font_direction);
    u8g2_DrawUTF8(&u8g2, (int16_t) round(_x), (int16_t) round(_y), _text);
}

void MenuDriver::draw(float _x, float _y, const char *_text, const uint8_t *_font, uint8_t _font_mode,
                      uint8_t _font_direction) {
    setFont(_font, _font_mode, _font_direction);
    u8g2_DrawUTF8(&u8g2, (int16_t) round(_x), (int16_t) round(_y), _text);
}

void MenuDriver::drawEnglish(float _x, float _y, const char *_text) {
    setFont(u8g2_font_wqy14_t_chinese3);
    u8g2_DrawStr(&u8g2, (int16_t) round(_x), (int16_t) round(_y), _text);
}

void MenuDriver::drawChinese(float _x, float _y, const char *_text) {
    setFont(u8g2_font_wqy14_t_chinese3);
    u8g2_DrawUTF8(&u8g2, (int16_t) round(_x), (int16_t) round(_y), _text);
}

void MenuDriver::drawVDottedLine(float _x, float _y, float _h) {
    for (unsigned char i = 0; i < (unsigned char) round(_h); i++) {
        if (i % 8 == 0 || (i - 1) % 8 == 0 || (i - 2) % 8 == 0)
            continue;
        u8g2_DrawPixel(&u8g2, (int16_t) round(_x), (int16_t) round(_y) + i);
    }
}

void MenuDriver::drawHDottedLine(float _x, float _y, float _l) {
    for (unsigned char i = 0; i < _l; i++) {
        if (i % 8 == 0 || (i - 1) % 8 == 0 || (i - 2) % 8 == 0)
            continue;
        u8g2_DrawPixel(&u8g2, (int16_t) round(_x) + i, (int16_t) round(_y));
    }
}

void MenuDriver::drawVLine(float _x, float _y, float _h) {
    u8g2_DrawVLine(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_h));
}

void MenuDriver::drawHLine(float _x, float _y, float _l) {
    u8g2_DrawHLine(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_l));
}

void MenuDriver::drawBMP(float _x, float _y, float _w, float _h, const uint8_t *_bitMap) {
    u8g2_DrawXBMP(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_w), (int16_t) round(_h), _bitMap);
}

void MenuDriver::drawBox(float _x, float _y, float _w, float _h) {
    u8g2_DrawBox(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_w), (int16_t) round(_h));
}

void MenuDriver::drawRBox(float _x, float _y, float _w, float _h, float _r) {
    u8g2_DrawRBox(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_w), (int16_t) round(_h),
                  (int16_t) round(_r));
}

void MenuDriver::drawFrame(float _x, float _y, float _w, float _h) {
    u8g2_DrawFrame(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_w), (int16_t) round(_h));
}

void MenuDriver::drawRFrame(float _x, float _y, float _w, float _h, float _r) {
    u8g2_DrawRFrame(&u8g2, (int16_t) round(_x), (int16_t) round(_y), (int16_t) round(_w), (int16_t) round(_h),
                    (int16_t) round(_r));
}

void MenuDriver::drawCheckMark(float x, float y, float w, float h) {
    float x0 = x + w * 0.1f;
    float y0 = y + h * 0.5f;

    float x1 = x + w * 0.4f;
    float y1 = y + h * 0.8f;

    float x2 = x + w * 0.9f;
    float y2 = y + h * 0.2f;

    u8g2_DrawLine(&u8g2, (int16_t) round(x0), (int16_t) round(y0), (int16_t) round(x1), (int16_t) round(y1));
    u8g2_DrawLine(&u8g2, (int16_t) round(x1), (int16_t) round(y1), (int16_t) round(x2), (int16_t) round(y2));
}