#ifndef HW_LIB_OLED_H
#define HW_LIB_OLED_H
#ifdef __cplusplus
extern "C" {
#endif

#include "stdint.h"


#define REFRESH_CALL_ENABLE 1 //使用DMA或者整体刷新函数


/**
 * @brief   OLED设备结构体
 */
typedef struct OLED_Dev OLED_T;

/**
 * @brief   OLED命令处理函数指针类型
 * @param   data: [输入] 数据指针
 * @param   len: [输入] 数据长度
 * @return  uint8_t 返回值
 */
typedef uint8_t (*OLED_CMD_t)(uint8_t *data, size_t len);

/**
 * @brief   OLED数据处理函数指针类型
 * @param   data: [输入] 数据指针
 * @param   len: [输入] 数据长度
 * @return  uint8_t 返回值
 */
typedef uint8_t (*OLED_DATA_t)(uint8_t *data, size_t len);

#if REFRESH_CALL_ENABLE

/**
 * @brief   OLED刷新函数指针类型
 */
typedef void (*OLED_REFRESH_t)(OLED_T *dev);

#endif

/**
 * @brief   OLED状态枚举
 */
typedef enum {
    IDLE,       /**< 空闲状态 */
    WRITE,      /**< 写入状态 */
    REFRESH,    /**< 刷新状态 */
} OLED_STATE_T;

/**
 * @brief   OLED设备结构体
 */
struct OLED_Dev {
    uint8_t *buf;           /**< 显示缓冲区指针 */
    uint8_t width;        /**< 显示宽度 */
    uint8_t height;       /**< 显示高度 */
    OLED_STATE_T state;   /**< OLED状态 */
    OLED_CMD_t cmd;         /**< OLED命令处理函数指针 */
    OLED_DATA_t data;       /**< OLED数据处理函数指针 */
#if REFRESH_CALL_ENABLE
    OLED_REFRESH_t call;    /**< OLED刷新函数指针 */
#endif
};

// OLED初始化指令数组
//const uint8_t initCmd[] = {
//        1, 0xAE,                // 关闭显示
//        2, 0xD5, 0x80,      // 设置显示时钟分频比/振荡器频率
//        2, 0xA8, 0x3F,      // 设置多路复用比率
//        2, 0xD3, 0x00,     // 设置显示偏移
//        2, 0x40, 0x00,   // 设置显示起始行
//        2, 0x8D, 0x14,   // 电荷泵设置
//        2, 0x20, 0x00,   // 设置内存寻址模式
//        1, 0xA0,              // 设置段重映射
//        1, 0xC0,              // 设置COM输出扫描方向
//        2, 0xDA, 0x12,   // 设置COM引脚硬件配置
//        2, 0x81, 0xCF,   // 设置对比度控制
//        2, 0xD9, 0xF1,   // 设置预充电周期
//        2, 0xDB, 0x20,   // 设置VCOMH取消电平
//        2, 0xA4, 0xA6,   // 整个显示打开
//        1, 0xAF               // 打开显示
//};
const uint8_t initCmd[] = {
        0xAE,                // 关闭显示
        0xD5, 0x80,      // 设置显示时钟分频比/振荡器频率
        0xA8, 0x3F,      // 设置多路复用比率
        0xD3, 0x00,     // 设置显示偏移
        0x40, 0x00,   // 设置显示起始行
        0x8D, 0x14,   // 电荷泵设置
        0x20, 0x00,   // 设置内存寻址模式
        0xA0,              // 设置段重映射
        0xC0,              // 设置COM输出扫描方向
        0xDA, 0x12,   // 设置COM引脚硬件配置
        0x81, 0xCF,   // 设置对比度控制
        0xD9, 0xF1,   // 设置预充电周期
        0xDB, 0x20,   // 设置VCOMH取消电平
        0xA4, 0xA6,   // 整个显示打开
        0xAF               // 打开显示
};

/**
 * @brief   OLED初始化
 * @param   dev: [输入] OLED设备指针
 * @return  void
 * @example OLED_Init(&oled_dev);
 */
void OLED_Init(OLED_T *dev);

/**
 * @brief   打开OLED显示
 * @param   dev: [输入] OLED设备指针
 * @return  void
 * @example OLED_ON(&oled_dev);
 */
void OLED_ON(OLED_T *dev);

/**
 * @brief   关闭OLED显示
 * @param   dev: [输入] OLED设备指针
 * @return  void
 * @example OLED_OFF(&oled_dev);
 */
void OLED_OFF(OLED_T *dev);

/**
 * @brief   打开或关闭OLED显示
 * @param   dev: [输入] OLED设备指针
 * @param   e: [输入] true表示打开,false表示关闭
 * @return  void
 * @example OLED_Turn(&oled_dev, true);
 */
void OLED_Turn(OLED_T *dev, bool e);

/**
 * @brief   刷新OLED显示
 * @param   dev: [输入] OLED设备指针
 * @return  void
 * @example OLED_Refresh(&oled_dev);
 */
void OLED_Refresh(OLED_T *dev);

/**
 * @brief   设置OLED显示开关
 * @param   dev: [输入] OLED设备指针
 * @param   e: [输入] true表示显示,false表示关闭
 * @return  void
 * @example OLED_DisplayTurn(&oled_dev, true);
 */
void OLED_DisplayTurn(OLED_T *dev, bool e);

/**
 * @brief   设置显示位置
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] X坐标
 * @param   y: [输入] Y坐标
 * @return  void
 * @example OLED_Set(&oled_dev, 0, 0);
 */
void OLED_Set(OLED_T *dev, uint8_t x, uint8_t y);

/**
 * @brief   取消显示位置
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] X坐标偏移量
 * @param   y: [输入] Y坐标偏移量
 * @return  void
 * @example OLED_RSet(&oled_dev, 10, 10);
 */
void OLED_RSet(OLED_T *dev, uint8_t x, uint8_t y);

/**
 * @brief   绘制直线
 * @param   dev: [输入] OLED设备指针
 * @param   x1: [输入] 起始点X坐标
 * @param   y1: [输入] 起始点Y坐标
 * @param   x2: [输入] 结束点X坐标
 * @param   y2: [输入] 结束点Y坐标
 * @return  void
 * @example OLED_DrawLine(&oled_dev, 0, 0, 20, 20);
 */
void OLED_DrawLine(OLED_T *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

/**
 * @brief   在 OLED 上绘制矩形
 * @param   dev: [输入] OLED 设备指针
 * @param   x1: [输入] 矩形左上角 x 坐标
 * @param   y1: [输入] 矩形左上角 y 坐标
 * @param   x2: [输入] 矩形右下角 x 坐标
 * @param   y2: [输入] 矩形右下角 y 坐标
 * @return  void
 * @example OLED_DrawRect(&oledDevice, 10, 10, 50, 30);
 **/
void OLED_DrawRect(OLED_T *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

/**
 * @brief   绘制圆
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] 圆心X坐标
 * @param   y: [输入] 圆心Y坐标
 * @param   r: [输入] 圆半径
 * @return  void
 * @example OLED_DrawCircle(&oled_dev, 30, 30, 10);
 */
void OLED_DrawCircle(OLED_T *dev, uint8_t x, uint8_t y, uint8_t r);

/**
 * @brief   显示字符
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] X坐标
 * @param   y: [输入] Y坐标
 * @param   chr: [输入] 要显示的字符
 * @param   size1: [输入] 字体大小
 * @return  void
 * @example OLED_ShowChar(&oled_dev, 0, 0, 'A', 16);
 */
void OLED_ShowChar(OLED_T *dev, uint8_t x, uint8_t y, uint8_t chr, uint8_t size1);

/**
 * @brief   显示字符串
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] X坐标
 * @param   y: [输入] Y坐标
 * @param   chr: [输入] 要显示的字符串
 * @param   size1: [输入] 字体大小
 * @return  void
 * @example OLED_ShowString(&oled_dev, 0, 0, "Hello", 16);
 */
void OLED_ShowString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *chr, uint8_t size1);

/**
 * @brief   设置显示起始坐标
 * @param   dev: [输入] OLED设备指针
 * @param   x: [输入] X坐标
 * @param   y: [输入] Y坐标
 * @return  void
 * @example OLED_SPos(&oled_dev, 0, 0);
 */
void OLED_SPos(OLED_T *dev, uint8_t x, uint8_t y);

/**
 * @brief   填充OLED显示缓冲区
 * @param   dev: [输入] OLED设备指针
 * @param   data: [输入] 填充数据
 * @return  void
 * @example OLED_Fill(&oled_dev, 0xFF);
 */
void OLED_Fill(OLED_T *dev, uint8_t data);

/**
 * @brief   清空OLED显示缓冲区
 * @param   dev: [输入] OLED设备指针
 * @return  void
 * @example OLED_CLS(&oled_dev);
 */
void OLED_CLS(OLED_T *dev);

#ifdef __cplusplus
}
#endif
#endif //HW_LIB_OLED_H