#include "ws2812.h"
#include "tim.h"
#include "stdlib.h"
#include "bsp/pwm.h"

/**
 * @file    ws2812.c
 * @brief   WS2812 LED驱动实现
 * @note    使用PWM+DMA方式驱动WS2812灯带
 */

/* 动态内存指针 */
static uint32_t* Pixel_Buf = NULL;
static uint16_t Pixel_NUM = 0;
static uint8_t Is_Initialized = 0;

/* WS2812时序码定义 
 * WS2812 时序要求 (800kHz, 1.25μs per bit):
 * - 1码: 高电平 0.8μs (64%), 低电平 0.4μs (32%)
 * - 0码: 高电平 0.4μs (32%), 低电平 0.8μs (64%)
 * ARR = 89, 周期 = 90 计数
 */
#define CODE_1 ((uint32_t)(90 * 0.64f))  /*!< 1码: 58 */
#define CODE_0 ((uint32_t)(90 * 0.32f))  /*!< 0码: 29 */

/**
 * @brief  初始化WS2812驱动，分配内存
 * @param  led_num: LED数量
 * @retval 0成功，1失败
 */
uint8_t WS2812_Init(uint16_t led_num)
{
    if (led_num == 0 || led_num > 1024) return 1; // 限制最大LED数量

    // 如果已经初始化，先释放旧的内存
    if (Is_Initialized)
    {
        WS2812_DeInit();
    }

    Pixel_NUM = led_num;
    uint32_t buf_size = (Pixel_NUM + 1) * 24; // 每个LED 24位 + 1行RESET

    Pixel_Buf = (uint32_t*)malloc(buf_size * sizeof(uint32_t));
    if (Pixel_Buf == NULL)
    {
        Pixel_NUM = 0;
        return 1;
    }
    
    // 初始化缓冲区为0
    for (uint32_t i = 0; i < buf_size; i++)
    {
        Pixel_Buf[i] = 0;
    }
    
    Is_Initialized = 1;
    
    // TIM1是高级定时器，必须使能主输出
    __HAL_TIM_MOE_ENABLE(&htim1);
    
    return 0;
}

/**
 * @brief  释放内存，停止DMA
 * @retval None
 */
void WS2812_DeInit(void)
{
    if (Pixel_Buf != NULL)
    {
        free(Pixel_Buf);
        Pixel_Buf = NULL;
    }
    Pixel_NUM = 0;
    Is_Initialized = 0;
    BSP_PWM_Stop(BSP_PWM_WS2812);
}

/**
 * @brief  设置单个LED的颜色
 * @param  LedId: LED序号
 * @param  R: 红色分量 0-255
 * @param  G: 绿色分量 0-255
 * @param  B: 蓝色分量 0-255
 * @retval None
 */
void WS2812_SetColor(uint8_t LedId, uint8_t R, uint8_t G, uint8_t B)
{
    if (!Is_Initialized || LedId >= Pixel_NUM || Pixel_Buf == NULL) return;

    uint32_t* led_data = Pixel_Buf + LedId * 24;

    // WS2812颜色顺序：GRB
    for (uint8_t i = 0; i < 8; i++)
        led_data[i] = (G & (1 << (7 - i))) ? CODE_1 : CODE_0;

    for (uint8_t i = 8; i < 16; i++)
        led_data[i] = (R & (1 << (15 - i))) ? CODE_1 : CODE_0;

    for (uint8_t i = 16; i < 24; i++)
        led_data[i] = (B & (1 << (23 - i))) ? CODE_1 : CODE_0;
}

/**
 * @brief  设置多个LED的颜色
 * @param  PixelArray: 颜色数组
 *         PixelArray[i][0]: LED的ID
 *         PixelArray[i][1]: R值
 *         PixelArray[i][2]: G值
 *         PixelArray[i][3]: B值
 * @retval None
 */
void WS2812_SetColors(uint8_t PixelArray[][4], uint16_t ArraySize)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;
    
    for (uint16_t i = 0; i < ArraySize; i++)
    {
        uint8_t id = PixelArray[i][0];
        uint8_t R = PixelArray[i][1];
        uint8_t G = PixelArray[i][2];
        uint8_t B = PixelArray[i][3];
        
        if (id < Pixel_NUM)
        {
            WS2812_SetColor(id, R, G, B);
        }
    }
}

/**
 * @brief  发送数据到WS2812灯带
 * @note   会自动添加RESET码
 * @retval None
 */
void WS2812_SendArray(void)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;

    // 设置RESET行（最后24个数据为0）
    uint32_t* reset_row = Pixel_Buf + Pixel_NUM * 24;
    for (uint8_t i = 0; i < 24; i++)
    {
        reset_row[i] = 0;
    }

    // 直接使用HAL库函数,绕过BSP层
    // HAL_TIM_PWM_Start_DMA(
    //     &htim1,
    //     TIM_CHANNEL_4,
    //     Pixel_Buf,
    //     (Pixel_NUM + 1) * 24
    // );
        BSP_PWM_Start_DMA(
        BSP_PWM_WS2812,
        Pixel_Buf,
        (Pixel_NUM + 1) * 24
    );

}

/**
 * @brief  清空所有LED（设置为黑色）
 * @retval None
 */
void WS2812_Clear(void)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;
    
    // 将所有LED数据清零
    for (uint32_t i = 0; i < Pixel_NUM * 24; i++)
    {
        Pixel_Buf[i] = 0;
    }
}

/**
 * @brief  设置所有LED为同一颜色
 * @param  R: 红色分量 0-255
 * @param  G: 绿色分量 0-255
 * @param  B: 蓝色分量 0-255
 * @retval None
 */
void WS2812_SetAll(uint8_t R, uint8_t G, uint8_t B)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;
    
    for (uint16_t i = 0; i < Pixel_NUM; i++)
    {
        WS2812_SetColor(i, R, G, B);
    }
}

/**
 * @brief  获取当前LED数量
 * @retval LED数量
 */
uint16_t WS2812_GetLedCount(void)
{
    return Pixel_NUM;
}

/*----------------------------------------------------------------------------
  状态指示灯功能实现 (低功耗/低干扰设计)
 *---------------------------------------------------------------------------*/

/* 状态指示灯控制结构 */
static WS2812_StatusCtrl_t StatusCtrl = {
    .status = WS2812_STATUS_IDLE,
    .lastStatus = WS2812_STATUS_IDLE,
    .blinkState = 0,
    .lastUpdateTick = 0,
    .lastCommTick = 0,
    .brightness = 25  /* 默认25%亮度,减少功耗对射频干扰 */
};

/* 通信超时时间(ms) */
static uint32_t CommTimeout_ms = 1000;

/* 闪烁周期(ms) */
#define BLINK_PERIOD_MS 500

/* LED更新最小间隔(ms) - 避免频繁DMA操作 */
#define LED_UPDATE_MIN_INTERVAL_MS 50

/**
 * @brief  设置LED状态指示
 * @param  status: LED状态 (WS2812_Status_t枚举)
 */
void WS2812_SetStatus(WS2812_Status_t status)
{
    StatusCtrl.status = status;
}

/**
 * @brief  获取当前LED状态
 * @retval 当前状态
 */
WS2812_Status_t WS2812_GetStatus(void)
{
    return StatusCtrl.status;
}

/**
 * @brief  通信成功通知
 */
void WS2812_NotifyCommSuccess(void)
{
    StatusCtrl.lastCommTick = HAL_GetTick();
}

/**
 * @brief  设置LED亮度
 * @param  brightness: 亮度百分比 (0-100)
 */
void WS2812_SetBrightness(uint8_t brightness)
{
    if (brightness > 100) brightness = 100;
    StatusCtrl.brightness = brightness;
}

/**
 * @brief  设置通信超时时间
 * @param  timeout_ms: 超时时间(ms)
 */
void WS2812_SetCommTimeout(uint32_t timeout_ms)
{
    CommTimeout_ms = timeout_ms;
}

/**
 * @brief  内部函数: 应用亮度并设置颜色
 * @param  R,G,B: 原始颜色值
 */
static void WS2812_SetColorWithBrightness(uint8_t R, uint8_t G, uint8_t B)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;
    
    /* 应用亮度缩放 */
    uint8_t adjR = (uint8_t)((uint16_t)R * StatusCtrl.brightness / 100);
    uint8_t adjG = (uint8_t)((uint16_t)G * StatusCtrl.brightness / 100);
    uint8_t adjB = (uint8_t)((uint16_t)B * StatusCtrl.brightness / 100);
    
    WS2812_SetColor(0, adjR, adjG, adjB);
}

/**
 * @brief  状态指示灯任务(非阻塞)
 * @param  currentTick: 当前系统tick(ms)
 * @note   自动管理闪烁、超时检测,对射频影响极小
 */
void WS2812_StatusTask(uint32_t currentTick)
{
    if (!Is_Initialized || Pixel_Buf == NULL) return;
    
    /* 控制更新频率,避免频繁DMA操作干扰射频 */
    if (currentTick - StatusCtrl.lastUpdateTick < LED_UPDATE_MIN_INTERVAL_MS) {
        return;
    }
    
    /* 检测通信超时: TX_OK/RX_OK状态下超时自动切换为ERROR */
    if (StatusCtrl.status == WS2812_STATUS_TX_OK || 
        StatusCtrl.status == WS2812_STATUS_RX_OK) {
        if (currentTick - StatusCtrl.lastCommTick > CommTimeout_ms) {
            StatusCtrl.status = WS2812_STATUS_ERROR;
        }
    }
    
    /* 计算闪烁状态 */
    uint8_t newBlinkState = ((currentTick / BLINK_PERIOD_MS) % 2) ? 1 : 0;
    
    /* 判断是否需要更新LED (状态变化或闪烁状态变化) */
    uint8_t needUpdate = 0;
    if (StatusCtrl.status != StatusCtrl.lastStatus) {
        needUpdate = 1;
        StatusCtrl.lastStatus = StatusCtrl.status;
    } else if (StatusCtrl.status == WS2812_STATUS_TX_OK ||
               StatusCtrl.status == WS2812_STATUS_RX_OK ||
               StatusCtrl.status == WS2812_STATUS_DEBUG) {
        /* 闪烁状态: 检查是否需要切换 */
        if (newBlinkState != StatusCtrl.blinkState) {
            needUpdate = 1;
        }
    }
    
    if (!needUpdate) return;
    
    StatusCtrl.blinkState = newBlinkState;
    StatusCtrl.lastUpdateTick = currentTick;
    
    /* 根据状态设置颜色 */
    switch (StatusCtrl.status) {
        case WS2812_STATUS_IDLE:
            /* 空闲: LED熄灭 */
            WS2812_SetColor(0, 0, 0, 0);
            break;
            
        case WS2812_STATUS_TX_OK:
        case WS2812_STATUS_RX_OK:
            /* 正常通信: 绿灯闪烁 */
            if (StatusCtrl.blinkState) {
                WS2812_SetColorWithBrightness(0, 255, 0);  /* 绿色 */
            } else {
                WS2812_SetColor(0, 0, 0, 0);  /* 熄灭 */
            }
            break;
            
        case WS2812_STATUS_ERROR:
            /* 异常: 红灯常亮 */
            WS2812_SetColorWithBrightness(255, 0, 0);  /* 红色 */
            break;
            
        case WS2812_STATUS_DEBUG:
            /* 调试: 黄灯闪烁 */
            if (StatusCtrl.blinkState) {
                WS2812_SetColorWithBrightness(255, 180, 0);  /* 黄色 */
            } else {
                WS2812_SetColor(0, 0, 0, 0);  /* 熄灭 */
            }
            break;
            
        default:
            WS2812_SetColor(0, 0, 0, 0);
            break;
    }
    
    /* 发送数据到LED */
    WS2812_SendArray();
}

