#include "task_debug.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "elog.h"
#include "heap.h"
#include "bsp_oled_sh1106.h"

static const char *TAG = "task_debug";
static uint8_t task_debug_oled_enabled = 0;
static tlsf_pool_statistics *dtcm_heap_statistics;
static tlsf_pool_statistics *ahb_heap_statistics;
static tlsf_pool_statistics *axi_heap_statistics;

#define TASK_DEBUG_INTERVAL_MS 100
#define TASK_DEBUG_LED_TOGGLE_INTERVAL_MS (500) //每500ms闪烁一次LED
#define TASK_DEBUG_OLED_REFRESH_INTERVAL_MS (200) //每200ms更新一次OLED显示
#define TASK_DEBUG_HEAP_USAGE_PRINT_INTERVAL_MS (1000*10) //每10秒打印一次堆状态信息
#define TASK_DEBUG_RUNTIME_STATS_PRINT_INTERVAL_MS (1000*10) //每10秒打印一次任务统计信息

static uint32_t task_debug_heap_usage_update_interval_ms = TASK_DEBUG_HEAP_USAGE_PRINT_INTERVAL_MS; //更新堆状态信息的时间间隔
static uint32_t task_debug_runtime_stats_update_interval_ms = TASK_DEBUG_RUNTIME_STATS_PRINT_INTERVAL_MS; //更新任务统计信息的时间间隔

void task_debug_led_toggle_routine(uint64_t millisecs_elapsed)
{
  if ((millisecs_elapsed % TASK_DEBUG_LED_TOGGLE_INTERVAL_MS == 0)) {
    HAL_GPIO_TogglePin(LED_B_GPIO_Port, LED_B_Pin);
  }
}

void task_debug_heap_usage_update_routine(uint64_t millisecs_elapsed)
{
  if ((millisecs_elapsed % task_debug_heap_usage_update_interval_ms == 0)) {
    dtcm_heap_statistics = get_dtcm_heap_statistics();
    ahb_heap_statistics = get_ahb_heap_statistics();
    axi_heap_statistics = get_axi_heap_statistics();
  }
}

void task_debug_runtime_stat_print_routine(uint64_t millisecs_elapsed)
{
  if ((millisecs_elapsed % TASK_DEBUG_RUNTIME_STATS_PRINT_INTERVAL_MS == 0) && millisecs_elapsed != 0) {
    char runtime_stats[512];
    vTaskGetRunTimeStats(runtime_stats);

    elog_debug(TAG, "runtime stats:\n%s\b", runtime_stats);
  }
}

void task_debug_heap_usage_print_routine(uint64_t millisecs_elapsed)
{
  if ((millisecs_elapsed % TASK_DEBUG_HEAP_USAGE_PRINT_INTERVAL_MS == 0) && millisecs_elapsed != 0) {
    elog_debug(TAG, "heap usage: xxx");
  }
}

void task_debug_oled_init(void)
{
  HAL_StatusTypeDef oled_status = bsp_oled_init(&hi2c2);

  if (oled_status != HAL_OK) {
    elog_warn(TAG, "oled initialization failed, oled print is disabled");
    return;
  }

  bsp_oled_6x8(0, 0, "DTCM     AXI     AHB");

  task_debug_oled_enabled = 1;
  task_debug_heap_usage_update_interval_ms = TASK_DEBUG_OLED_REFRESH_INTERVAL_MS;
  task_debug_runtime_stats_update_interval_ms = TASK_DEBUG_OLED_REFRESH_INTERVAL_MS; //提高状态信息的刷新速度
}

void task_debug_oled_refresh_routine(uint64_t millisecs_elapsed)
{
  if ((millisecs_elapsed % TASK_DEBUG_OLED_REFRESH_INTERVAL_MS == 0) && task_debug_oled_enabled == 1) {
    uint32_t free_dtcm_heap = dtcm_heap_statistics->free_size;
    uint32_t free_axi_heap = axi_heap_statistics->free_size;
    uint32_t free_ahb_heap = ahb_heap_statistics->free_size;

    char str[40];
    sprintf(str, "%2d.%dk  %3d.%dk  %3d.%dk", (int)(free_dtcm_heap / 1024), (int)((free_dtcm_heap % 1024) / 102),
                                              (int)(free_axi_heap / 1024),  (int)((free_axi_heap % 1024) / 102),
                                              (int)(free_ahb_heap / 1024),  (int)((free_ahb_heap % 1024) / 102));
    bsp_oled_6x8(1, 0, str);

    bsp_oled_update();
  }
}

void task_debug_entry(void *param)
{
  uint64_t millisecs_elapsed = 0;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xPeriod = pdMS_TO_TICKS(TASK_DEBUG_INTERVAL_MS);

  task_debug_oled_init();

  while (1) {
    task_debug_led_toggle_routine(millisecs_elapsed); //闪烁LED 用于指示低优先级任务正在运行

    task_debug_heap_usage_update_routine(millisecs_elapsed); //更新堆状态信息
    //更新任务统计信息

    task_debug_runtime_stat_print_routine(millisecs_elapsed); //定时打印任务统计信息
    task_debug_heap_usage_print_routine(millisecs_elapsed); //定时打印堆占用信息
    task_debug_oled_refresh_routine(millisecs_elapsed); //定时刷新OLED显示

    millisecs_elapsed += TASK_DEBUG_INTERVAL_MS;
    vTaskDelayUntil(&xLastWakeTime, xPeriod);
  }
}

void task_debug_create(void *param)
{
  xTaskCreate(task_debug_entry, "task_debug", 4096 / sizeof(StackType_t), param, 15, NULL);
}
