#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_timer.h"
#include "driver/i2s.h"
#include "baidu_chat_agents_engine.h"

#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0))
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#else
#include "board.h"
#include "periph_wifi.h"
#include "sys/socket.h"
#include "netdb.h"
#include "audio_sys.h"
#include "audio_mem.h"
#define BRTC_DEMO_STATS
#define BRTC_DEMO_VIDEO
#define BRTC_DEMO_AUDIO
#endif

#ifdef BRTC_DEMO_AUDIO
#include "audio_element.h"
#include "audio_pipeline.h"
#include "audio_common.h"
#include "i2s_stream.h"
#include "aec_stream.h"
#include "algorithm_stream.h"
#include "raw_stream.h"
#include "audio_thread.h"
#define BRTC_AUDIO_FRAME_SIZE 640
#define BRTC_AUDIO_RATE 16000
#define BRTC_AUDIO_BITS 16
#define BRTC_AUDIO_CHANNEL 1

#define AUDIO_QUEUE_SIZE 32 // 增加队列大小
#define MIN_BUFFERED_PACKETS 4  // 提高最小缓冲包数
#define MAX_BUFFERED_PACKETS 20  // 设置最大缓冲限制

static QueueHandle_t audio_queue = NULL;
static int is_first_audio = 1;
static volatile bool audio_underrun = false;
static audio_element_handle_t i2s_stream_writer = NULL;
static audio_element_handle_t i2s_stream_reader = NULL;
typedef struct {
    uint8_t* data;
    size_t len;
} audio_data_t;
#endif

#include "audio_mem.h"
#ifdef BRTC_DEMO_VIDEO
#include "video_raw_data.h"
#include "audio_mutex.h"
#include "esp_camera.h"
#include "esp_err.h"
#include "esp_jpeg_enc.h"
#include "esp_jpeg_dec.h"
#include "esp_jpeg_common.h"
#include "esp_lcd_panel_ops.h"
#define VIDEO_NAL_DATA jpeg_640x480_frame
static esp_lcd_panel_handle_t panel_handle = NULL;
static bool g_enable_visual = true;                  //true:开启视觉互动模式
//static int  g_vision_mode = VISION_MODE_IMAGE;      //开启视频流理解模式
static int  g_vision_mode = VISION_MODE_STREAM;
static bool g_enable_image_generate = true;
static bool g_send_image = false;
#endif
#include "esp_log.h"
#include "esp_heap_caps.h"
//==============================================================================
// General Definitions and Global Variables
//==============================================================================
#define BRTC_LOG printf
static bool brtcDemoRunning = false;
static bool brtcDemoCapture = false;
static bool brtcDemoAudio   = false;
static bool g_brtc_license_abnormal_exit = false;    //true:表示鉴权失败退出
static bool g_brtc_agent_login_failed_exit = false;  //true:表示智能体登陆失败退出
static bool g_brtc_agent_disconnect_exit = false;    //true:表示网络断开退出
static bool g_enable_enhance_query = false;          //true:开启query增强模式
/* 使用全局引擎变量，方便各任务访问 */
static BaiduChatAgentEngine *g_engine = NULL;
static uint64_t time_last_asr = 0;
static bool volatile stop_video_send_flag = false;
static bool g_brtc_need_exit = false;

const char* TAG = "main";
//==============================================================================
// object_vision_prompt definition
//==============================================================================
static const char* object_vision_prompt =
    "# 你是一位资深的早教老师，每次会收到一张来自幼儿园或小学生的物品照片。请识别照片中的物品并输出它的中文汉字和英文单词名称及如字典一样详细的中英文释义。\\\\n\\\\n"
    "## 中文释义要求：\\\\n"
    "1. 根据<汉字>进行回答\\\\n"
    "2. 回答<汉字>的笔画数量，格式：`strokenumber：{number}`\\\\n"
    "3. 回答<汉字>的偏旁部首，格式：`radical：{word}`\\\\n"
    "4. 按次序提供<汉字>的不同词义，并为每个词义提供1～2个包含该汉字的词语\\\\n"
    "5. 保证内容简洁明了，易于低龄学生理解\\\\n\\\\n"
    "## 英文释义要求：\\\\n"
    "1. 根据<单词>进行回答\\\\n"
    "2. 回答<单词>的美式音标，格式：`soundmark_us：{phonetic}`\\\\n"
    "3. 回答<单词>的英式音标，格式：`soundmark_en：{phonetic}`\\\\n"
    "4. 按次序提供<单词>的不同词性及中文翻译\\\\n"
    "5. 提供1～2句包含<单词>的示例语句\\\\n"
    "6. 保证内容简洁明了，易于低龄学生理解\\\\n\\\\n"
    "## 输出格式示例（如收到树的图片）：\\\\n"
    "树。\\\\n"
    "tree。\\\\n"
    "((CUSTOM:\\\\n"
    "{\\\\n"
    "  \\\\\\\"name_ch\\\\\\\": \\\\\\\"花\\\\\\\",\\\\n"
    "  \\\\\\\"name_en\\\\\\\": \\\\\\\"flower\\\\\\\",\\\\n"
    "  \\\\\\\"comments_ch\\\\\\\": {\\\\n"
    "    \\\\\\\"name\\\\\\\": \\\\\\\"花\\\\\\\",\\\\n"
    "    \\\\\\\"strokenumber\\\\\\\": 7,\\\\n"
    "    \\\\\\\"radical\\\\\\\": \\\\\\\"艹\\\\\\\",\\\\n"
    "    \\\\\\\"comment\\\\\\\": \\\\\\\"1. 种子植物的有性繁殖器官，由花瓣、花萼、花托、花蕊组成。词语：一朵花。\\\\\\\\\\\\n2. 可供观赏的植物。词语：花草。\\\\\\\",\\\\n"
    "    \\\\\\\"example_sentence\\\\\\\": \\\\\\\"学校里种了许许多多五颜六色的花。\\\\\\\"\\\\n"
    "  },\\\\n"
    "  \\\\\\\"comments_en\\\\\\\": {\\\\n"
    "    \\\\\\\"name\\\\\\\": \\\\\\\"flower\\\\\\\",\\\\n"
    "    \\\\\\\"soundmark_us\\\\\\\": \\\\\\\"/ˈflaʊ.ɚ/\\\\\\\",\\\\n"
    "    \\\\\\\"soundmark_en\\\\\\\": \\\\\\\"/ˈflaʊ.ə/\\\\\\\",\\\\n"
    "    \\\\\\\"comment\\\\\\\": \\\\\\\"n. 花；精华；开花植物\\\\\\\\\\\\nv. 开花；繁荣；成熟\\\\\\\",\\\\n"
    "    \\\\\\\"example_sentence\\\\\\\": \\\\\\\"There are many different kinds of flowers in the garden.\\\\\\\"\\\\n"
    "  }\\\\n"
    "}\\\\n"
    "))\\\\n\\\\n"
    "## 请按以下格式应答：\\\\n"
    "<汉字>:{识别出的中文名称}。\\\\n"
    "<单词>:{识别出的英文名称}。\\\\n"
    "((CUSTOM:\\\\n"
    "{\\\\n"
    "  \\\\\\\"name_ch\\\\\\\": \\\\\\\"{中文名称}\\\\\\\",\\\\n"
    "  \\\\\\\"name_en\\\\\\\": \\\\\\\"{英文名称}\\\\\\\",\\\\n"
    "  \\\\\\\"comments_ch\\\\\\\": {\\\\n"
    "    \\\\\\\"name\\\\\\\": \\\\\\\"{中文名称}\\\\\\\",\\\\n"
    "    \\\\\\\"strokenumber\\\\\\\": {笔画数},\\\\n"
    "    \\\\\\\"radical\\\\\\\": \\\\\\\"{偏旁部首}\\\\\\\",\\\\n"
    "    \\\\\\\"comment\\\\\\\": \\\\\\\"{详细释义}\\\\\\\",\\\\n"
    "    \\\\\\\"example_sentence\\\\\\\": \\\\\\\"{示例句子}\\\\\\\"\\\\n"
    "  },\\\\n"
    "  \\\\\\\"comments_en\\\\\\\": {\\\\n"
    "    \\\\\\\"name\\\\\\\": \\\\\\\"{英文名称}\\\\\\\",\\\\n"
    "    \\\\\\\"soundmark_us\\\\\\\": \\\\\\\"{美式音标}\\\\\\\",\\\\n"
    "    \\\\\\\"soundmark_en\\\\\\\": \\\\\\\"{英式音标}\\\\\\\",\\\\n"
    "    \\\\\\\"comment\\\\\\\": \\\\\\\"{词性和释义}\\\\\\\",\\\\n"
    "    \\\\\\\"example_sentence\\\\\\\": \\\\\\\"{示例句子}\\\\\\\"\\\\n"
    "  }\\\\n"
    "}\\\\n"
    "))";
/******************************************************************************
 ******************************************************************************
 **
 ** Wi-Fi Section
 **
 ******************************************************************************
 ******************************************************************************/
#define BRTC_WIFI_SSID      "BD"
#define BRTC_WIFI_PASS      "Qaswhk.168"
//#define BRTC_WIFI_SSID      "ESP-Audio"
//#define BRTC_WIFI_PASS      "esp123456"
//
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0))
static EventGroupHandle_t wifi_event_group = NULL;
const static int CONNECTED_BIT = BIT0;

static esp_err_t brtc_demo_event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            esp_wifi_connect();
            xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
            break;
        default:
            break;
    }
    return ESP_OK;
}

void brtc_demo_init_wifi(void) {
    tcpip_adapter_init();
    /* 创建事件组 */
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_init(brtc_demo_event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = BRTC_WIFI_SSID,
            .password = BRTC_WIFI_PASS,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
}
#else
void brtc_demo_init_wifi(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0))
    ESP_ERROR_CHECK(esp_netif_init());
#else
    tcpip_adapter_init();
#endif

    esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
    esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
#ifdef BRTC_DEMO_VIDEO
    panel_handle = audio_board_lcd_init(set, NULL);
#endif

    periph_wifi_cfg_t wifi_cfg = {
        .wifi_config.sta.ssid = BRTC_WIFI_SSID,
        .wifi_config.sta.password = BRTC_WIFI_PASS,
    };
    esp_periph_handle_t wifi_handle = periph_wifi_init(&wifi_cfg);
    esp_periph_start(set, wifi_handle);
    periph_wifi_wait_for_connected(wifi_handle, portMAX_DELAY);
    AUDIO_MEM_SHOW(TAG);
}
#endif

#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0))
void brtc_demo_link_lwip() {
    char addr[64] = "";
    char serv[6] = "3";
    struct addrinfo hints, *res = NULL;
    int af = AF_INET;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = af;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

    getaddrinfo(addr, serv, &hints, &res);
}
#endif
/******************************************************************************
 ******************************************************************************
 **
 ** Audio Section
 **
 ******************************************************************************
 ******************************************************************************/

#include "audio_sys.h"
static void brtc_task_monitor(void * arg) {
    while (1) {
        vTaskDelay(5000 / portTICK_PERIOD_MS);
       audio_sys_get_real_time_stats();
       AUDIO_MEM_SHOW(TAG);
    }
    BRTC_LOG("brtc_task_monitor exit\n");
    vTaskDelete(NULL);
}

static void audio_render_task(void * arg) {
    static uint8_t silence[320] = {0};
    TickType_t last_audio_time = xTaskGetTickCount();
    const TickType_t audio_timeout = pdMS_TO_TICKS(100); // 100ms超时
    audio_data_t audio_data;
    while (brtcDemoRunning) {
        int queue_count = uxQueueMessagesWaiting(audio_queue);
        if (is_first_audio == 1) {  // 初始缓冲策略
            if (queue_count < MIN_BUFFERED_PACKETS) {
                vTaskDelay(pdMS_TO_TICKS(10));  // 减少延迟时间
                continue;
            }
            is_first_audio = 0;
            BRTC_LOG("Initial buffering complete, starting playback\n");
        }
        /*
        // 自适应缓冲策略
        if (queue_count == 0 && !is_first_audio) {
            // 检测欠载情况
            if (!audio_underrun) {
                audio_underrun = true;
                BRTC_LOG("Audio underrun detected\n");
            }
            // 播放静音数据，避免爆音
            audio_element_output(i2s_stream_writer, (char*)silence, sizeof(silence));
            //vTaskDelay(pdMS_TO_TICKS(10));
            // 如果长时间没有数据，重新进入缓冲模式
            if ((xTaskGetTickCount() - last_audio_time) > audio_timeout) {
                is_first_audio = 1;
                BRTC_LOG("Audio timeout, re-entering buffering mode\n");
            }
            continue;
        }*/
        // 正常播放
        if (xQueueReceive(audio_queue, &audio_data, pdMS_TO_TICKS(10)) == pdPASS) {
            //audio_underrun = false;
            //last_audio_time = xTaskGetTickCount();
            // 检查队列深度，动态调整
            //if (queue_count > MAX_BUFFERED_PACKETS) {
                // 队列过深，可能有延迟，考虑丢弃一些包
                //BRTC_LOG("Queue depth too high: %d\n", queue_count);
            //}
            audio_element_output(i2s_stream_writer, (char*)audio_data.data, audio_data.len);
            free(audio_data.data);
        }
    }
    // 清理剩余的队列数据
    while (xQueueReceive(audio_queue, &audio_data, 0) == pdPASS) {
        free(audio_data.data);
    }
    BRTC_LOG("audio_render_task exit\n");
    vTaskDelete(NULL);
}

#ifdef BRTC_DEMO_AUDIO
typedef struct {
    audio_pipeline_handle_t rec_pipe;
    audio_element_handle_t  i2s_read;
    audio_element_handle_t  aec_stream;
    audio_element_handle_t  raw_stream;
    bool                    running;
} rec_res_t;

static rec_res_t rec_res;

static void destroy_record_pipe(void)
{
    if (rec_res.rec_pipe) {
        if (rec_res.running) {
            rec_res.running = false;
            audio_pipeline_stop(rec_res.rec_pipe);
            audio_pipeline_wait_for_stop(rec_res.rec_pipe);
        } else {
            if (rec_res.i2s_read) {
                audio_element_deinit(rec_res.i2s_read);
            }
            if (rec_res.aec_stream) {
                audio_element_deinit(rec_res.aec_stream);
            }
            if (rec_res.raw_stream) {
                audio_element_deinit(rec_res.raw_stream);
            }
        }
        audio_pipeline_terminate(rec_res.rec_pipe);
        audio_pipeline_deinit(rec_res.rec_pipe);
    }
    memset(&rec_res, 0, sizeof(rec_res));
}

#define BREAD_ON_NULL_HD(hd) if (hd == NULL) {break;}

static int i2s_read_cb(audio_element_handle_t el, char *buf, int len, TickType_t wait_time, void *ctx)
{
    size_t bytes_read = audio_element_input(rec_res.i2s_read, buf, len);
    if (bytes_read <= 0) {
        ESP_LOGE(TAG, "I2S read failed, %d", len);
    } 
    return bytes_read;
}
#include "es7210.h"
static int create_rec_pipeline(void)
{
    do {
        audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
        rec_res.rec_pipe = audio_pipeline_init(&pipeline_cfg);
        BREAD_ON_NULL_HD(rec_res.rec_pipe);

        i2s_stream_cfg_t i2s_r_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(CODEC_ADC_I2S_PORT, 16000, 32, AUDIO_STREAM_READER);
        i2s_r_cfg.task_stack = -1;
        i2s_stream_set_channel_type(&i2s_r_cfg, I2S_CHANNEL_FMT_ONLY_LEFT);
        rec_res.i2s_read = i2s_stream_init(&i2s_r_cfg);

        es7210_adc_set_gain(ES7210_INPUT_MIC3, GAIN_0DB);
        es7210_adc_set_gain(ES7210_INPUT_MIC2 | ES7210_INPUT_MIC1, GAIN_33DB);

        algorithm_stream_cfg_t algo_config = ALGORITHM_STREAM_CFG_DEFAULT();
        algo_config.input_format = "RM";
        algo_config.sample_rate = 16000;
        algo_config.out_rb_size = 256;
        rec_res.aec_stream = algo_stream_init(&algo_config);
        audio_element_set_music_info(rec_res.aec_stream, 16000, 1, ALGORITHM_STREAM_DEFAULT_SAMPLE_BIT);
        audio_element_set_read_cb(rec_res.aec_stream, i2s_read_cb, NULL);
        audio_element_set_input_timeout(rec_res.aec_stream, portMAX_DELAY);

        raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
        raw_cfg.type = AUDIO_STREAM_WRITER;
        raw_cfg.out_rb_size = 2 * 1024;
        rec_res.raw_stream = raw_stream_init(&raw_cfg);
        BREAD_ON_NULL_HD(rec_res.raw_stream);
        audio_element_set_output_timeout(rec_res.raw_stream, portMAX_DELAY);
        algo_config.mode = ALGORITHM_STREAM_USE_AEC | ALGORITHM_STREAM_USE_AGC;
        const char *link_tag[] = { "aec", "raw"};
        audio_pipeline_register(rec_res.rec_pipe, rec_res.aec_stream , link_tag[0]);
        audio_pipeline_register(rec_res.rec_pipe, rec_res.raw_stream , link_tag[1]);
        audio_pipeline_link(rec_res.rec_pipe, &link_tag[0], sizeof(link_tag)/sizeof(link_tag[0]));
        audio_pipeline_run(rec_res.rec_pipe);
        rec_res.running = true;
        return 0;
    } while (0);
    destroy_record_pipe();
    return -1;
}

static void brtc_demo_audio(void * arg) {
    size_t rs = 0;
    char abuf[BRTC_AUDIO_FRAME_SIZE] = {0};

    audio_board_handle_t board_handle = audio_board_init();
    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);

    i2s_stream_cfg_t i2w_cfg = I2S_STREAM_CFG_DEFAULT();
    i2w_cfg.type = AUDIO_STREAM_WRITER;
    i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(32, 1);
    slot_cfg.slot_mask = (i2s_std_slot_mask_t)1;
    i2w_cfg.task_stack = -1;
    slot_cfg.data_bit_width = 16;
    slot_cfg.slot_bit_width = 32;
    i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(BRTC_AUDIO_RATE);
    i2w_cfg.std_cfg.clk_cfg = clk_cfg;
    i2w_cfg.std_cfg.slot_cfg = slot_cfg;
    // i2w_cfg.buffer_len = 1024 * 16;
    i2s_stream_writer = i2s_stream_init(&i2w_cfg);
    create_rec_pipeline();
    BRTC_LOG("brtc_demo_audio:%d\n", __LINE__);

    audio_element_run(i2s_stream_writer);
    //xTaskCreate(brtc_task_monitor, "brtc_task_monitor", 1024 * 4, NULL, 3, NULL);
    xTaskCreate(audio_render_task, "audio_render_task", 1024 * 4, NULL, 15, NULL);

    if (rec_res.running) {
        while (brtcDemoRunning) {
            rs = raw_stream_read(rec_res.raw_stream, (char *)abuf, BRTC_AUDIO_FRAME_SIZE);
            if (brtcDemoAudio) {
                if (rs > 0 && g_engine) {
                    baidu_chat_agent_engine_send_audio(g_engine, (const uint8_t*)abuf, rs);
                }
            } else {
                vTaskDelay(10 / portTICK_PERIOD_MS);
            }
        }
    }
    destroy_record_pipe();
    audio_element_handle_t writer = i2s_stream_writer;
    i2s_stream_writer = NULL;
    audio_element_deinit(writer);
    BRTC_LOG("brtc_demo_audio exit\n");
    vTaskDelete(NULL);
    
}
#endif // BRTC_DEMO_AUDIO

/******************************************************************************
 ******************************************************************************
 **
 ** Video Section
 **
 ******************************************************************************
 ******************************************************************************/
#ifdef BRTC_DEMO_VIDEO
#define BRTC_DEMO_LCD_H    (320)
#define BRTC_DEMO_LCD_V    (240)
#define BRTC_DEMO_JPG_W    (320)
#define BRTC_DEMO_JPG_H    (240)
//#define BRTC_DEMO_LCD_H    (640)
//#define BRTC_DEMO_LCD_V    (480)
//#define BRTC_DEMO_JPG_W    (640)
//#define BRTC_DEMO_JPG_H    (480)

typedef struct {
    uint8_t *buffer;
    size_t size;
} brtc_demo_video_pkg_t;

static uint8_t *dsp_buffer = NULL;
static uint8_t *rgb_buffer = NULL;
static uint8_t *cam_buffer = NULL;
static int cam_image_size = 0;
static QueueHandle_t brtc_demo_video_pkg_queue;

typedef struct {
    uint8_t *buffer;
    size_t size;
    void* handle;
    xSemaphoreHandle lock;
} brtc_enc_jpg_s;
static brtc_enc_jpg_s jpg_enc;

#define CAM_PIN_PWDN -1
#define CAM_PIN_RESET -1
#define CAM_PIN_XCLK 40
#define CAM_PIN_SIOD -1
#define CAM_PIN_SIOC -1
#define CAM_PIN_D7 39
#define CAM_PIN_D6 41
#define CAM_PIN_D5 42
#define CAM_PIN_D4 12
#define CAM_PIN_D3 3
#define CAM_PIN_D2 14
#define CAM_PIN_D1 47
#define CAM_PIN_D0 13
#define CAM_PIN_VSYNC 21
#define CAM_PIN_PCLK 11

static camera_config_t camera_config = {
    .pin_pwdn = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sscb_sda = CAM_PIN_SIOD,
    .pin_sscb_scl = CAM_PIN_SIOC,
    .pin_d7 = CAM_PIN_D7,
    .pin_d6 = CAM_PIN_D6,
    .pin_d5 = CAM_PIN_D5,
    .pin_d4 = CAM_PIN_D4,
    .pin_d3 = CAM_PIN_D3,
    .pin_d2 = CAM_PIN_D2,
    .pin_d1 = CAM_PIN_D1,
    .pin_d0 = CAM_PIN_D0,
    .pin_vsync = CAM_PIN_VSYNC,
    .pin_href = CAM_PIN_HREF,
    .pin_pclk = CAM_PIN_PCLK,
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,
#if 0
    .pixel_format = PIXFORMAT_JPEG,
#else
     .pixel_format = PIXFORMAT_YUV422,
#endif
    .frame_size = FRAMESIZE_QVGA,  //320*240 
    //.frame_size = FRAMESIZE_VGA,     //640*480
    .jpeg_quality = 12,
    .fb_count = 2,
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
    .xclk_freq_hz = 20000000,
};
#endif // BRTC_DEMO_VIDEO

#ifdef BRTC_DEMO_VIDEO
#define JPEG_DEC_OUTPUT_FORMAT JPEG_PIXEL_FORMAT_RGB565_BE

// JPEG marker definitions
#define JPEG_MARKER_SOI  0xD8  // Start of Image
#define JPEG_MARKER_SOF0 0xC0  // Baseline DCT
#define JPEG_MARKER_SOF2 0xC2  // Progressive DCT
#define JPEG_MARKER_FF   0xFF  // Marker prefix

/**
 * Parse JPEG data to determine if it's baseline or progressive
 * @param data Pointer to JPEG data
 * @param len Length of JPEG data
 * @return 0 for baseline, 1 for progressive, -1 for error/unknown
 */
static int brtc_demo_jpeg_parse_profile(const unsigned char *data, size_t len)
{
    if (data == NULL || len < 4) {
        ESP_LOGE(TAG, "Invalid JPEG data");
        return -1;
    }

    // Check for JPEG SOI marker (0xFF 0xD8)
    if (data[0] != JPEG_MARKER_FF || data[1] != JPEG_MARKER_SOI) {
        ESP_LOGE(TAG, "Not a valid JPEG file (missing SOI marker)");
        return -1;
    }

    // Scan through JPEG data looking for SOF markers
    // Skip SOI marker (first 2 bytes)
    size_t i = 2;
    while (i < len - 1) {
        // Look for marker prefix (0xFF)
        if (data[i] == JPEG_MARKER_FF) {
            uint8_t marker = data[i + 1];

            // Skip padding bytes (0xFF 0x00)
            if (marker == 0x00) {
                i += 2;
                continue;
            }

            // Check for baseline DCT (SOF0)
            if (marker == JPEG_MARKER_SOF0) {
                ESP_LOGI(TAG, "JPEG profile: Baseline");
                return 0;
            }

            // Check for progressive DCT (SOF2)
            if (marker == JPEG_MARKER_SOF2) {
                ESP_LOGI(TAG, "JPEG profile: Progressive");
                return 1;
            }

            // Skip other markers (they have 2-byte length field)
            if (marker >= 0xE0 && marker <= 0xEF) {
                // APP markers (0xFFE0-0xFFEF) - skip length field
                if (i + 3 < len) {
                    uint16_t length = (data[i + 2] << 8) | data[i + 3];
                    i += 2 + length;
                    continue;
                }
            } else if (marker == 0xC4 || marker == 0xDB || marker == 0xDA ||
                       marker == 0xC0 || marker == 0xC1 || marker == 0xC2 ||
                       marker == 0xC3 || marker == 0xC5 || marker == 0xC6 ||
                       marker == 0xC7 || marker == 0xC8 || marker == 0xC9 ||
                       marker == 0xCA || marker == 0xCB || marker == 0xCC ||
                       marker == 0xCD || marker == 0xCE || marker == 0xCF) {
                // SOF markers or other markers with length field
                if (i + 3 < len) {
                    uint16_t length = (data[i + 2] << 8) | data[i + 3];
                    i += 2 + length;
                    continue;
                }
            }
        }
        i++;
    }

    ESP_LOGW(TAG, "Could not determine JPEG profile (SOF marker not found)");
    return -1;
}

int brtc_demo_jpeg_dec(unsigned char **pixels, const unsigned char *data, size_t len, int* width, int *height)
{
    // Print head 16 bytes of received JPEG data
    size_t head_len = len < 16 ? len : 16;
    char head_hex[64] = {0};
    int pos = 0;
    for (size_t i = 0; i < head_len && pos < sizeof(head_hex) - 3; i++) {
        pos += snprintf(head_hex + pos, sizeof(head_hex) - pos, "%02X ", data[i]);
    }
    ESP_LOGI(TAG, "JPEG data head (%zu bytes): %s", head_len, head_hex);

    // Print tail 16 bytes of received JPEG data
    size_t tail_start = len > 16 ? len - 16 : 0;
    char tail_hex[64] = {0};
    pos = 0;
    for (size_t i = tail_start; i < len && pos < sizeof(tail_hex) - 3; i++) {
        pos += snprintf(tail_hex + pos, sizeof(tail_hex) - pos, "%02X ", data[i]);
    }
    if (len > 16) {
        ESP_LOGI(TAG, "JPEG data tail (16 bytes): %s", tail_hex);
    } else {
        ESP_LOGI(TAG, "JPEG data tail (same as head, total %zu bytes): %s", len, tail_hex);
    }

    // Parse JPEG data to determine profile (baseline or progressive)
    int profile = brtc_demo_jpeg_parse_profile(data, len);
    if (profile == 0) {
        ESP_LOGD(TAG, "Processing Baseline JPEG");
    } else if (profile == 1) {
        ESP_LOGD(TAG, "Processing Progressive JPEG");
    } else {
        ESP_LOGW(TAG, "JPEG profile detection failed, continuing anyway");
    }
    AUDIO_MEM_SHOW(TAG);
    static jpeg_dec_handle_t jpeg_dec = NULL;
    int ret = 0;
    if (jpeg_dec == NULL) {
        // Generate default configuration
        jpeg_dec_config_t config = DEFAULT_JPEG_DEC_CONFIG();
        config.output_type = JPEG_DEC_OUTPUT_FORMAT;
        config.scale.width = BRTC_DEMO_LCD_H;
        config.scale.height = BRTC_DEMO_LCD_V;
        // Empty handle to jpeg_decoder
        // Create jpeg_dec
        ret = jpeg_dec_open(&config, &jpeg_dec);
        if (jpeg_dec == NULL) {
            ESP_LOGE(TAG, "Got an error by jpeg_dec_open, ret:%d", ret);
            return -1;
        }
    }
    int outbuf_len = 0;
    jpeg_dec_io_t jpeg_io = {
        .inbuf = (uint8_t*)data,
        .inbuf_len = len,
    };
    jpeg_dec_header_info_t out_info = {0};
    // Parse jpeg picture header and get picture for user and decoder
    ret = jpeg_dec_parse_header(jpeg_dec, &jpeg_io, &out_info);
    if (ret < 0) {
        ESP_LOGE(TAG, "Got an error by jpeg_dec_parse_header, ret:%d", ret);
        return ret;
    }
    // Calloc out_put data buffer and update inbuf ptr and inbuf_len
    if (JPEG_DEC_OUTPUT_FORMAT== JPEG_PIXEL_FORMAT_RGB565_LE
        || JPEG_DEC_OUTPUT_FORMAT == JPEG_PIXEL_FORMAT_RGB565_BE) {
        outbuf_len = out_info.height * out_info.width * 2;
    } else if (JPEG_DEC_OUTPUT_FORMAT == JPEG_PIXEL_FORMAT_RGB888) {
        outbuf_len = out_info.height * out_info.width * 3;
    } else {
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "jpeg_dec_image:%d (%d * %d)", outbuf_len, out_info.width, out_info.height);
    unsigned char *out_buf = heap_caps_aligned_alloc(16, outbuf_len, MALLOC_CAP_SPIRAM);
    jpeg_io.outbuf = out_buf;
    int inbuf_consumed = jpeg_io.inbuf_len - jpeg_io.inbuf_remain;
    jpeg_io.inbuf = (unsigned char *)(data + inbuf_consumed);
    jpeg_io.inbuf_len = jpeg_io.inbuf_remain;
    *width = out_info.width;
    *height = out_info.height;

    // Start decode jpeg raw data
    ret = jpeg_dec_process(jpeg_dec, &jpeg_io);
    if (ret < 0) {
        ESP_LOGE(TAG, "jpeg_dec_fail:%d", ret);
        return ret;
    }
    *pixels = out_buf;
    jpeg_dec_close(jpeg_dec);
    jpeg_dec = 0;
    AUDIO_MEM_SHOW(TAG);
    return ESP_OK;
}

static void brtc_demo_lcd_draw(esp_lcd_panel_handle_t panel_handle, uint8_t *image) {
    uint32_t lines_num = 40;
    AUDIO_MEM_SHOW(TAG);
    for (int i = 0; i < BRTC_DEMO_LCD_V / lines_num; ++i) {
        esp_lcd_panel_draw_bitmap(panel_handle, 0, i * lines_num, BRTC_DEMO_LCD_H,
                    lines_num + i * lines_num, image + BRTC_DEMO_LCD_H * i * lines_num * 2);
    }
}

static void brtc_demo_disp_jpg(const char *img, int len) {
    // BRTC_LOG("brtc demo jpg type:%d, w:%d, h:%d, len = %d\n", imgtype, width, height, len);
    unsigned char *lcd_pixels = NULL;
    int width = 0;
    int height = 0;
    brtc_demo_jpeg_dec(&lcd_pixels, (const unsigned char*)img, len, &width, &height);
    if (lcd_pixels != NULL) {
        for (int i = 0; i < BRTC_DEMO_LCD_V && i < height; i++) {
            memcpy(dsp_buffer + i * BRTC_DEMO_LCD_H * 2, lcd_pixels + i * width * 2,  width * 2);
        }
        free(lcd_pixels);
        brtc_demo_lcd_draw(panel_handle, dsp_buffer);
    }
}

#define GET_RGB565_R(x) (((((x) >> 11) & 0x1F) << 3) | (((x) >> 8) & 0x07))
#define GET_RGB565_G(x) (((((x) >> 5) & 0x3F) << 2) | (((x) >> 3) & 0x03))
#define GET_RGB565_B(x) ((((x) & 0x1F) << 3) | (((x) & 0x1C) >> 2))
#define SWAP_EDIAN(x)   (((x) << 8) | ((x) >> 8))
static void brtc_demo_rgb565_2_rgb888_row(uint8_t *src565, uint8_t *dst888, int width) {
    for (int i = 0; i < width; ++i) {
        uint16_t rgb = *(uint16_t*)src565;
        rgb = SWAP_EDIAN(rgb);
        dst888[0] = GET_RGB565_R(rgb);
        dst888[1] = GET_RGB565_G(rgb);
        dst888[2] = GET_RGB565_B(rgb);
        dst888 += 3;
        src565 += 2;
    }
}

static int brtc_demo_jpeg_enc(unsigned char *outbuf, const unsigned char *inbuf, size_t in_len) {
    int jpeg_size = 0;

    if (!jpg_enc.handle) {
        jpeg_enc_config_t jpeg_enc_cfg = DEFAULT_JPEG_ENC_CONFIG();
        jpeg_enc_cfg.width = BRTC_DEMO_JPG_W;
        jpeg_enc_cfg.height = BRTC_DEMO_JPG_H;
        if (camera_config.pixel_format == PIXFORMAT_RGB565) {
            jpeg_enc_cfg.src_type = JPEG_PIXEL_FORMAT_RGB888;
        } else if (camera_config.pixel_format == PIXFORMAT_YUV422) {
            jpeg_enc_cfg.src_type = JPEG_PIXEL_FORMAT_YCbYCr;
        }
        jpeg_enc_cfg.subsampling = JPEG_SUBSAMPLE_420;
        jpeg_enc_cfg.quality = 30;
        jpeg_enc_cfg.rotate = JPEG_ROTATE_0D;
        jpeg_enc_cfg.task_enable = false;
        jpeg_enc_cfg.hfm_task_priority = 13;
        jpeg_enc_cfg.hfm_task_core = 1;
        jpeg_enc_open(&jpeg_enc_cfg, &jpg_enc.handle);
        if (jpg_enc.handle == NULL) {
            return jpeg_size;
        }
    }
    int out_len = BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 2;
    jpeg_enc_process(jpg_enc.handle, inbuf, in_len, outbuf, out_len, &jpeg_size);
    return jpeg_size;
}

static void brtc_demo_video_decoder_and_render(void * arg) {

    while (1) {
        brtc_demo_video_pkg_t pkg;
        if (xQueueReceive(brtc_demo_video_pkg_queue, &pkg, portMAX_DELAY) == pdPASS) {
            ESP_LOGI(TAG, "Receive size %d", pkg.size);
            if (pkg.size > 0) {
                brtc_demo_disp_jpg((const char *)pkg.buffer, pkg.size);
                free(pkg.buffer);
                pkg.buffer = NULL;
                pkg.size = 0;
            }
        }
    }

}
static void brtc_demo_cam_task(void * arg) {
    //int maxbytes = BRTC_DEMO_LCD_H * BRTC_DEMO_LCD_V * 2;
    int maxbytes = BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 2;
    while (brtcDemoRunning) {
        if (brtcDemoCapture) {
            camera_fb_t *pic = esp_camera_fb_get();
            if (pic) {
                if (pic->buf && pic->len > 0) {
                    mutex_lock(jpg_enc.lock);
                    memcpy(cam_buffer, pic->buf, pic->len < maxbytes ? pic->len : maxbytes);
                    cam_image_size = pic->len;
                    mutex_unlock(jpg_enc.lock);
                }
                esp_camera_fb_return(pic);
            } else {
                vTaskDelay(pdMS_TO_TICKS(1000));
            }
        } else {
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    BRTC_LOG("cam task exit(%d)\n", uxTaskGetStackHighWaterMark(NULL));
    vTaskDelete(NULL);
}

static void brtc_demo_vsend_task(void * arg) {
    while (brtcDemoRunning) {
        uint32_t cur_time = esp_timer_get_time() / 1000;
        if (brtcDemoCapture) {
            int image_size = 0;
            mutex_lock(jpg_enc.lock);
            if (camera_config.pixel_format == PIXFORMAT_JPEG) {
                memcpy(rgb_buffer, cam_buffer, cam_image_size);
                jpg_enc.buffer = rgb_buffer;
                jpg_enc.size = cam_image_size;
            } if (camera_config.pixel_format == PIXFORMAT_YUV422) {
                memcpy(rgb_buffer, cam_buffer, cam_image_size);
                image_size = cam_image_size;
            } else {
                uint8_t* src565 = cam_buffer;
                uint8_t* dst888 = rgb_buffer;
                for (int i = 0; i < BRTC_DEMO_LCD_V; i++) {
                    brtc_demo_rgb565_2_rgb888_row(src565, dst888, BRTC_DEMO_JPG_W);
                    src565 += BRTC_DEMO_LCD_H * 2;
                    dst888 += BRTC_DEMO_JPG_W * 3;
                }
                image_size = BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 3;
            }
            mutex_unlock(jpg_enc.lock);
            if (camera_config.pixel_format != PIXFORMAT_JPEG) {
                jpg_enc.size = brtc_demo_jpeg_enc(jpg_enc.buffer, rgb_buffer, image_size);
            }
            if  (g_vision_mode == VISION_MODE_IMAGE) {
                //baidu_chat_agent_engine_send_video(g_engine, VIDEO_NAL_DATA, sizeof(VIDEO_NAL_DATA));
                if (jpg_enc.size && g_send_image) {
                    //trace_size("Vsend", jpg_enc.size);
                    g_send_image = false;
                    baidu_chat_agent_engine_send_video(g_engine, jpg_enc.buffer, jpg_enc.size);
                }
            } else {
            if (jpg_enc.size) {
                //trace_size("Vsend", jpg_enc.size);
                baidu_chat_agent_engine_send_video(g_engine, jpg_enc.buffer, jpg_enc.size);
            }
        }
        }
        //vTaskDelay(pdMS_TO_TICKS(100 - (esp_timer_get_time() / 1000 - cur_time)));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    vTaskDelete(NULL);
}
#endif // BRTC_DEMO_VIDEO

// Callback for receiving remote video frames from RTC
static void onVideoData(const char *img, int len, RtcImageType imgtype, int width, int height){
    BRTC_LOG("Received video data %p length: %d, width: %d, height: %d\n", img, len, width, height);
    if (!brtcDemoRunning) {
        return;
    }
    //trace_size("VRecv", len);

#ifdef BRTC_DEMO_VIDEO
   AUDIO_MEM_SHOW(TAG);
    brtc_demo_video_pkg_t pkg;
    pkg.buffer = jpeg_calloc_align(len,16);
    memcpy(pkg.buffer, img, len);
    pkg.size = len;
    xQueueSend(brtc_demo_video_pkg_queue, &pkg, 10);
#endif
}
/******************************************************************************
 ******************************************************************************
 **
 ** Baidu RTC ChatAgent Section
 **
 ******************************************************************************
 ******************************************************************************/ 
void auto_send_video(void *h);
// Callback implementations
void onErrorCallback(int errCode, const char* errMsg) {
    BRTC_LOG("Error occurred. Code: %d, Message: %s\n", errCode, errMsg);
}

void onCallStateChangeCallback(AGentCallState state) {
    BRTC_LOG("Call state changed: %d\n", state);
    switch (state) {
        case AGENT_LOGIN_SUCCESS:
            g_brtc_agent_login_failed_exit = false;
            BRTC_LOG("AGENT_LOGIN_SUCCESS: %d\n", state);
            break;
        case AGENT_LOGIN_FAIL:
            BRTC_LOG("AGENT_LOGIN_FAIL: %d, please exit and re-login\n", state);
            g_brtc_agent_login_failed_exit = true;
            break;
        case AGENT_CALL_FAIL:
            BRTC_LOG("AGENT_CALL_FAIL: %d, please re-login\n", state);
            g_brtc_agent_login_failed_exit = true;
            break;
        case AGENT_LOGIN_OUT_SUCCESS:
            BRTC_LOG("AGENT_LOGIN_OUT_SUCCESS: %d\n", state);
            break;
        default:
            BRTC_LOG("Unknown call state: %d\n", state);
            break;
    }
}

void onConnectionStateChangeCallback(AGentConnectState state) {
    BRTC_LOG("Connection state changed: %d\n", state);
        switch (state) {
        case AGENT_CONNECTION_STATE_CONNECTING:
            BRTC_LOG("AGENT_CONNECTION_STATE_CONNECTING: %d\n", state);
            break;
        case AGENT_CONNECTION_STATE_CONNECTED:
            BRTC_LOG("AGENT_CONNECTION_STATE_CONNECTED: %d\n", state);
            g_brtc_agent_login_failed_exit = true;
            break;
        case AGENT_CONNECTION_STATE_DISCONNECTED:
            BRTC_LOG("AGENT_CONNECTION_STATE_DISCONNECTED: %d, please attempt to reconnect\n", state);
            g_brtc_agent_disconnect_exit = true; 
            // Call to clean up resources before reconnecting by calling baidu_chat_agent_engine_destroy
            // Make sure to call baidu_chat_agent_engine_destroy before brtc_demo_start
            break;
        default:
            BRTC_LOG("Unknown call state: %d\n", state);
            break;
    }
}

void onUserAsrSubtitleCallback(const char* text, size_t text_len, bool final, const ASRExtInfo* info){
    BRTC_LOG("User ASR subtitle: '%.*s', Name: %s, Emotion: %s, Score: %d, SessionId: %llu, (isFinal: %d)\n",
           (int)text_len, text,
           info->speakerName,
           info->emotion,
           info->similarity,
           info->sessionId,
           final);
    time_last_asr = esp_timer_get_time();
}

void onFunctionCall(const char* id, const char* params) {
    BRTC_LOG("onFunctionCall, id: %s, params: %s\n", id, params);
//调节音量的打印信息：onFunctionCall, id: 1749438356763, params: {"session_id":"1749438356763","content":{"function_name":"adjust_volume","parameter_list":[{"mode":"down"}]}}
//onFunctionCall回调的params是json格式，请基于业务需要进行解析所需要的字段。
}

void onMediaSetup(void) {
    BRTC_LOG("Media setup completed.\n");
    baidu_chat_agent_engine_send_text_to_TTS(g_engine, "我来了");
    // 设置增强Query, 如需要
    if (g_enable_enhance_query) {
        baidu_chat_agent_engine_set_enhance_query(g_engine, 3, "我现在在成都", "用2个字回答问题");
    }
#ifdef BRTC_DEMO_VIDEO
    if (g_enable_visual) {
        if (g_vision_mode == VISION_MODE_STREAM) {
            baidu_chat_agent_engine_update_visual_mode(g_engine, VISION_MODE_STREAM);
            // 若开启视频视觉理解, 可在此处开始周期性 (1000ms一次) 采集发送JPEG图片
            // auto_send_video(NULL);
        } else {
            baidu_chat_agent_engine_update_visual_mode(g_engine, VISION_MODE_IMAGE);
        }
    } else if (g_enable_image_generate) {
        // 如果不使用视觉理解图片模式，则可以开启图片生成
        baidu_chat_agent_engine_send_event_to_agent(g_engine, AGENT_EVENT_ENABLE_MEDIA_GENERATE);
    }
#endif
    time_last_asr = esp_timer_get_time();
    //brtcDemoAudio = true;
}

void onAIAgentSubtitle(const char* text, bool final) {
    BRTC_LOG("onAIAgentSubtitle.%s, final:%d\n", text, final);
    time_last_asr = esp_timer_get_time();
}

void onAIAgentSpeaking(bool speaking) {
    info("AI Agent Speaking State Change: %s ===\n", speaking ? "START" : "STOP");
    if (speaking) {
        // 清空旧的音频数据
        /*
        audio_data_t audio_data;
        while (xQueueReceive(audio_queue, &audio_data, 0) == pdPASS) {
            free(audio_data.data);
        }*/
        is_first_audio = 1;
        audio_underrun = false;
        brtcDemoCapture = false;
        brtcDemoAudio = true;
    } else {
        brtcDemoCapture = true;
    }
}

void onAudioPlayerOp(const char* path, bool start) {
    BRTC_LOG("onAudioPlayerOp. path:%s, start:%d\n", path, start);
}

void onAudioData(const uint8_t* data, size_t len) {
    if (!brtcDemoRunning) {
        return;
    }
#ifdef BRTC_DEMO_AUDIO
    if (i2s_stream_writer && len > 0) {
        // 检查队列是否已满，避免内存泄漏
        /*
        if (uxQueueSpacesAvailable(audio_queue) == 0) {
            // 队列满时，丢弃最老的数据
            audio_data_t old_data;
            if (xQueueReceive(audio_queue, &old_data, 0) == pdPASS) {
                free(old_data.data);
            }
            BRTC_LOG("Audio queue full, dropping oldest packet\n");
        }*/
        audio_data_t audio_data;
        audio_data.data = malloc(len);
        if (audio_data.data == NULL) {
            BRTC_LOG("Failed to allocate memory for audio data\n");
            return;
        }
        memcpy(audio_data.data, data, len);
        audio_data.len = len;
        if (xQueueSend(audio_queue, &audio_data, portMAX_DELAY) != pdPASS) {// 使用阻塞发送
            free(audio_data.data);
            BRTC_LOG("Failed to queue audio data\n");
        }
    }
#endif
}
/*
void onVideoData(const uint8_t *data, size_t len, RtcImageType imgtype, int width, int height) {
    BRTC_LOG("Received video data of length: %zu, width: %d, height: %d\n", len, width, height);
    if (imgtype == RTC_IMAGE_TYPE_JPEG) {
        // render to display
#ifdef DUMP_VIDEO_FRAME
        if (len > 0) {
            char filename[64] = {0};
            os_sprintf(filename, "0:video_frame_%04d.jpeg", (uint32_t)os_jiffies() % 9999);
            dumpVideoFrame(filename, data, len);
            jpeg_photo_explain(filename, 320, 240);
}
#endif
        //jpeg_photo_renderer(data, len, 320, 240);
    }
}
*/
void onLicenseResult (bool result) {
    printf("onLicenseResult: %d\n", result);
    if (!result) {
        BRTC_LOG("onLicenseResult: failed\n");
        g_brtc_license_abnormal_exit = true;
    }
}

/**
 * @brief 模型识别到视觉意图后没用户请求一张图片，用户可从即时从相机采集一张图片上传。
 *  当然用户也可以先上传图片，再进行询问“图片中能看到什么”
 *
 */
void onVisionImageRequest() {
    BRTC_LOG("onVisionImageRequest send a image to vision llm ...\n" );
#ifdef BRTC_DEMO_VIDEO
    if  (g_vision_mode == VISION_MODE_IMAGE) {
        //baidu_chat_agent_engine_send_video(g_engine, VIDEO_NAL_DATA, sizeof(VIDEO_NAL_DATA));
        g_send_image = true;
    }
#endif
}

/**
 * @brief 视觉图片上传完成回调
 *
 * @param name 上传的文件名，可忽略
 */
void onVisionImageAck(const char* name) {
    printf("onVisionImageAck file: %s ...\n", name);
}

void onMediaGenerateResult(const char* result) {
    if (result && strlen(result) > 0) {
        BRTC_LOG("onMediaGenerateResult content: %s\n", result);
    }
}

void onAgentEventUpdated(const uint8_t* data, size_t len) {
    BRTC_LOG("onAgentEventUpdated: %s, len:%d\r\n", data, len);
   if (len == strlen(AGENT_EVENT_REMOTE_PLAYER_BEGIN) &&
    strncmp((const char*)data, AGENT_EVENT_REMOTE_PLAYER_BEGIN, len) == 0) {

    } else if(len == strlen(AGENT_EVENT_REMOTE_PLAYER_END) &&
    strncmp((const char*)data, AGENT_EVENT_REMOTE_PLAYER_END, len) == 0) {

	}
}

//
char *config = "{"
    "\"app_id\": \"%s\", "
    "\"config\": \"{"
        "\\\"llm\\\": \\\"%s\\\", "
        "\\\"llm_token\\\": \\\"no\\\", "
        "\\\"rtc_ac\\\": \\\"%s\\\", "
        "\\\"lang\\\": \\\"%s\\\", "
        "\\\"tts_end_delay_ms\\\": 100, "
        "\\\"remote_music_player\\\": true, "
        "\\\"enable_visual\\\": \\\"%s\\\", "
        "\\\"dfda\\\": \\\"%s\\\""
    "}\", "
    "\"quick_start\": true"
"}";

void setUserParameters(AgentEngineParams *params) {
    snprintf(params->agent_platform_url, sizeof(params->agent_platform_url), "%s", SERVER_HOST_ONLINE);
    snprintf(params->appid, sizeof(params->appid), "%s", BDCloudDefaultRTCAppID);
    snprintf(params->userId, sizeof(params->userId), "%s", "12345678");
    snprintf(params->cer, sizeof(params->cer), "%s", "./a.cer");
    snprintf(params->workflow, sizeof(params->workflow), "%s", "VoiceChat");
    //snprintf(params->license_key, sizeof(params->license_key), "%s", "a37483c9c21d44928b23a4fb03a8dcbd");
    snprintf(params->license_key, sizeof(params->license_key), "%s", "28f3a3193fe8413aa6e6de561c2f8e64");
    snprintf(params->config, sizeof(params->config), "%s", config);
    printf("params->config:%s\n", params->config);
    params->instance_id = 10373;
    params->verbose = true;
    params->enable_local_agent =true;
    params->enable_voice_interrupt = true;
    params->disable_send_mute_data = false;
    params->level_voice_interrupt = 80;
    params->AudioInChannel = 1;
    params->AudioInFrequency = 16000;
    params->region = REGION_MAINLAND; //国内：REGION_MAINLAND 国外：REGION_OVERSEAS
    //params->region = REGION_AMERICA;
#ifdef BRTC_DEMO_VIDEO
    if(g_enable_visual || g_enable_image_generate) {
        params->enable_video = true; // 视觉理解场景开启视频
    }
#endif
}

void brtc_demo_init(void) {
    AUDIO_MEM_SHOW(TAG);
    BaiduChatAgentEvent events = {
        .onError = onErrorCallback,
        .onCallStateChange = onCallStateChangeCallback,
        .onConnectionStateChange = onConnectionStateChangeCallback,
        .onUserAsrSubtitle = onUserAsrSubtitleCallback,
        .onAIAgentSubtitle = onAIAgentSubtitle,
        .onAIAgentSpeaking = onAIAgentSpeaking,
        .onFunctionCall = onFunctionCall,
        .onAudioPlayerOp = onAudioPlayerOp,
        .onMediaSetup = onMediaSetup,
        .onAudioData = onAudioData,
        .onVideoData = onVideoData,
        .onLicenseResult = onLicenseResult,
        .onVisionImageRequest = onVisionImageRequest,
        .onVisionImageAck = onVisionImageAck,
        .onMediaGenerateResult = onMediaGenerateResult,
        .onAgentEventUpdated = onAgentEventUpdated,
    };

    BaiduChatAgentEngine* engine = baidu_create_chat_agent_engine(&events);
    if (!engine) {
        BRTC_LOG("Engine initialization failed.\n");
        return;
    }
    g_engine = engine; // 保存全局变量供后续使用
	stop_video_send_flag = false;
    g_brtc_license_abnormal_exit = false;
    g_brtc_agent_login_failed_exit = false;
    g_brtc_agent_disconnect_exit = false;

    AgentEngineParams agentParams;
    memset(&agentParams, 0, sizeof(agentParams));
    setUserParameters(&agentParams);

    BRTC_LOG("Logging in...\n");
    int result = baidu_chat_agent_engine_init(engine, &agentParams);
    if (result != 200) {
        BRTC_LOG("Failed to log in. Error code: %d\n", result);
        baidu_chat_agent_engine_destroy(engine);
        g_engine = NULL;
        return;
    }

    baidu_chat_agent_engine_call(engine);
    AUDIO_MEM_SHOW(TAG);
}

void brtc_demo_start(void* arg) {
    brtc_demo_init();
    vTaskDelete(NULL);
}

// 视觉接口验证，循环发送同一帧JPEG静态数据
void auto_send_video(void *h)
{
    if (stop_video_send_flag) {
        return;
    }
    BRTC_LOG("send video nal ...\n");
    //baidu_chat_agent_engine_send_video(g_engine, VIDEO_NAL_DATA, sizeof(VIDEO_NAL_DATA));
    brtc_sdk_do_async(auto_send_video, NULL, 1000);
}

#ifdef BRTC_DEMO_STATS
static bool brtc_stats_looping = false;
static int looping_c = 0;

static void brtc_demo_stats(void *pv) {
    BRTC_LOG("ASR time_task: %lld %lld %lld  \n", esp_timer_get_time(), time_last_asr, (esp_timer_get_time() - time_last_asr));
    while (1) {
        looping_c++;
        if (g_brtc_license_abnormal_exit || g_brtc_agent_login_failed_exit ||
            ((time_last_asr != 0) && ((esp_timer_get_time() - time_last_asr) > 300 * 1000000))) {
            if (g_brtc_license_abnormal_exit) {
                BRTC_LOG("brtc license confirm failed ...\n");
            } else if (g_brtc_agent_login_failed_exit) {
                BRTC_LOG("brtc lagent login failed ...\n");
            } else {
                BRTC_LOG("ASR no ai response over 5 min ...\n");
            }
            BaiduChatAgentEngine *agentEngine = g_engine;
            g_engine = NULL;
			stop_video_send_flag = true;
            brtcDemoCapture = false;
            baidu_chat_agent_engine_send_text_to_TTS(agentEngine, "ByeBye,我退下了");
            vTaskDelay(10000 / portTICK_PERIOD_MS);
            time_last_asr = 0;
            brtcDemoRunning = false;
            BRTC_LOG("ASR baidu_chat_agent_engine_destroy ...\n");
            if (agentEngine) {
                BRTC_LOG("baidu_chat_agent_engine_destroy__1\n");
                baidu_chat_agent_engine_destroy(agentEngine);
                agentEngine = NULL;
                BRTC_LOG("baidu_chat_agent_engine_destroy__2\n");
            }
            break;
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0))
    brtc_demo_link_lwip();
#endif

    vTaskDelete(NULL);
}
#endif

/******************************************************************************
 ******************************************************************************
 **
 ** send text Section: Entry Point
 **
 ******************************************************************************
 ******************************************************************************/
static char at_query_text[4096];
void agent_send_text(void * text) {
    baidu_chat_agent_engine_send_text(g_engine, (char *)text);
}

void send_text(const char *text) {
    if (text && os_strlen(text) > 0) {
        memset(at_query_text, 0, sizeof(at_query_text) -1);
        strncpy(at_query_text, text, sizeof(at_query_text) -1);
        BRTC_LOG("at query text:%s\r\n", at_query_text);
        //os_task_create("brtc_task_send_text", agent_send_text, (void*)at_query_text, OS_TASK_PRIORITY_NORMAL, 0, NULL, 16 * 1024);
	    xTaskCreatePinnedToCore(agent_send_text, "brtc_task_send_text", 1024 * 16, (void*)at_query_text, 10, NULL, 0);
    }
}

static int g_send_frame_num = 0;
void send_video_frame() {
    if (stop_video_send_flag || g_send_frame_num-- == 0) {
        return;
    }
    BRTC_LOG("%s:%d send_frame_num:%d\n",__FUNCTION__,__LINE__, g_send_frame_num);
#ifdef BRTC_DEMO_VIDEO 
    baidu_chat_agent_engine_send_video(g_engine, VIDEO_NAL_DATA, sizeof(VIDEO_NAL_DATA));
#endif
    brtc_sdk_do_async(send_video_frame, NULL, 1000);
}

// 视觉接口验证，循环发送同一帧 JPEG 静态数据
void send_video(const char *frame_num)
{
    // 从argv 获取发送帧数
    g_send_frame_num = atoi(frame_num);
    BRTC_LOG("%s:%d start send_frame_num:%d\n",__FUNCTION__,__LINE__, g_send_frame_num);
    send_video_frame();
}

void agent_update_prompt(void * text) {
	baidu_chat_agent_engine_send_event_to_agent(g_engine, (char *)text);
}

// model_type  2:视觉理解 (其它类型模型prompt更新暂未支持)
static const char g_updata_vision_prompt_cmd[] = "%s{\\\"model_type\\\":\\\"%s\\\",\\\"prompt\\\":\\\"%s\\\"}";
void update_vision_prompt(char *prompt) {
	int prompt_len = prompt ? strlen(prompt) : 0;
	int total_len = prompt_len + strlen(g_updata_vision_prompt_cmd) + 64;
	char *update_prompt_cmd = (char *)malloc(total_len);

	if (update_prompt_cmd) {
		snprintf(update_prompt_cmd, total_len, g_updata_vision_prompt_cmd,
					AGENT_EVENT_UPDATE_SYSTEM_PROMPT, "2", prompt);
	} else {
		BRTC_LOG("update_vision_prompt malloc failed\r\n");
		return;
	}
	strncpy(at_query_text, update_prompt_cmd, sizeof(at_query_text) -1);
	free(update_prompt_cmd);
}

void update_prompt(const char *prompt_mode) {
    int mode = atoi(prompt_mode);
    memset(at_query_text, 0, sizeof(at_query_text) -1);
    if (mode == 2) {
        update_vision_prompt(object_vision_prompt);
    } else if (mode == 0) {
        // reset vision prompt
        update_vision_prompt("");
    }

    BRTC_LOG("update prompt text:%s\r\n", at_query_text);
    //os_task_create("brtc_task_update_prompt", agent_update_prompt, (void*)at_query_text, OS_TASK_PRIORITY_NORMAL, 0, NULL, 16 * 1024);
	xTaskCreatePinnedToCore(agent_update_prompt, "brtc_task_update_prompt", 1024 * 16, (void*)at_query_text, 10, NULL, 0);
}


static void set_agent_mode(void *agent_mode) {
    char *mode_str = (char *)agent_mode;
    if (!g_engine || !brtcDemoRunning) {
        BRTC_LOG("Engine not initialized\n");
        return;
    }

    int mode = atoi(mode_str);
    if (mode < 0 || mode > 2) {
        BRTC_LOG("Invalid mode: %d\n", mode);
        return;
    }
#ifdef BRTC_DEMO_VIDEO 
    if (mode == 0) {
        // 普通语音交互模式;视频理解+图片模式
        g_enable_visual = true;
        g_enable_image_generate = false;
        baidu_chat_agent_engine_send_event_to_agent(g_engine, AGENT_EVENT_DISABLE_MEDIA_GENERATE);
        baidu_chat_agent_engine_update_visual_mode(g_engine, VISION_MODE_IMAGE);
    } else if (mode == 1) {
        // 视觉理解+视频流模式
        g_enable_visual = true;
        g_enable_image_generate = false;
        baidu_chat_agent_engine_send_event_to_agent(g_engine, AGENT_EVENT_DISABLE_MEDIA_GENERATE);
        baidu_chat_agent_engine_update_visual_mode(g_engine, VISION_MODE_STREAM);
    } else if (mode == 2) {
        // 图片生成模式
        g_enable_visual = false;
        g_enable_image_generate = true;
        baidu_chat_agent_engine_update_visual_mode(g_engine, VISION_MODE_IMAGE);
        baidu_chat_agent_engine_send_event_to_agent(g_engine, AGENT_EVENT_ENABLE_MEDIA_GENERATE);
    }
#endif    
}


// cmd: send_text  param1:文本内容
// cmd: update_prompt  param1: 2:更新视觉更解prompt 0:恢复默认prompt
// cmd: send_video  param1:发送视频帧数
// cmd: set_mode  param1: 0:普通语音交互模式 1:视觉理解模式 2:图片生成模式
// cmd: restart param1: appid

void brtc_cmd(const char *cmd, char *argv[], uint32_t argc) {
    if (argc < 2) {
        BRTC_LOG("cmd format error\r\n");
        return;
    }
    char *cmd_type = argv[0];
    char* param = argv[1];
    if (param == NULL || strlen(param) == 0)
    {
        BRTC_LOG("cmd param error\r\n");
        return;
    }

    TaskFunction_t func = NULL;
    BRTC_LOG("cmd_type:%s, param:%s\r\n", cmd_type, param);
    if (strcmp(cmd_type, "send_text") == 0) {
        send_text(param);
    } else if (strcmp(cmd_type, "update_prompt") == 0) {
        update_prompt(param);
    } else if (strcmp(cmd_type, "send_video") == 0) {
        send_video(param);
    } else if (strcmp(cmd_type, "set_mode") == 0) {
        func = set_agent_mode;
    } else if (strcmp(cmd_type, "restart") == 0) {
        app_main();
    } else {
        BRTC_LOG("unknown cmd:%s\r\n", cmd_type);
        return;
    }

    if (func == NULL) {
        return;
    }
    //os_task_create("brtc_task_update_prompt", func, (void*)param, OS_TASK_PRIORITY_NORMAL, 0, NULL, 16 * 1024);
	xTaskCreatePinnedToCore(func, "brtc_task_update_prompt", 1024 * 16, (void*)param, 10, NULL, 0);
}

/******************************************************************************
 ******************************************************************************
 **
 ** app_main Section: Entry Point and Task Orchestration
 **
 ******************************************************************************
 ******************************************************************************/
void brtc_demo_create_task(TaskFunction_t func, const char* task_name, uint32_t stack_size, UBaseType_t uxPriority) {
    StackType_t *stack_buf = heap_caps_calloc(1, stack_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    if (!stack_buf) {
        BRTC_LOG("Failed to allocate stack for %s\n", task_name);
        return;
    }
    TaskParameters_t params = {
        .pvTaskCode = func,
        .pcName = task_name,
        .usStackDepth = stack_size / sizeof(portSTACK_TYPE),
        .pvParameters = NULL,
        .uxPriority = uxPriority,
        .puxStackBuffer = stack_buf,
        .xRegions = { {0} }
    };
    xTaskCreateRestrictedPinnedToCore(&params, NULL, 1);
}

void app_main(void)
{
    brtcDemoRunning = true;
    brtcDemoCapture = false;
    brtcDemoAudio   = false;

    audio_queue = xQueueCreate(AUDIO_QUEUE_SIZE, sizeof(audio_data_t));
#ifdef BRTC_DEMO_STATS
    if (!brtc_stats_looping) {
        //audio_thread_create(NULL, "brtc_demo_stats", brtc_demo_stats, NULL, 8 * 1024, 10, true, 0);
        xTaskCreatePinnedToCore(brtc_demo_stats, "brtc_demo_stats", 1024 * 8, NULL, 10, NULL, 0);
    }
#endif

    brtc_demo_init_wifi();
    brtc_demo_create_task(brtc_demo_start, "brtc_demo_start", 1024 * 16, 10);

#ifdef BRTC_DEMO_AUDIO
      //audio_thread_create(NULL, "brtc_demo_audio", brtc_demo_audio, NULL, 8 * 1024, 20, true, 0);
      xTaskCreatePinnedToCore(brtc_demo_audio, "brtc_demo_audio", 1024 * 8, NULL, 10, NULL, 0);
#endif

#ifdef BRTC_DEMO_VIDEO
    memset(&jpg_enc, 0, sizeof(brtc_enc_jpg_s));

    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        BRTC_LOG("brtc demo camera init failed\n");
        return;
    }
    AUDIO_MEM_SHOW(TAG);

    dsp_buffer = heap_caps_calloc(1, BRTC_DEMO_LCD_H * BRTC_DEMO_LCD_V * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    rgb_buffer = heap_caps_aligned_alloc(16, BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 3, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    //cam_buffer = heap_caps_calloc(1, BRTC_DEMO_LCD_H * BRTC_DEMO_LCD_V * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    cam_buffer = heap_caps_calloc(1, BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    jpg_enc.buffer = heap_caps_aligned_alloc(16, BRTC_DEMO_JPG_W * BRTC_DEMO_JPG_H * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    jpg_enc.lock = mutex_create();
    // create send image task, control the send fps
    audio_thread_create(NULL, "brtc_demo_vsend", brtc_demo_vsend_task, NULL, 20 * 1024, 15, true, 1);

    // create camera capture task
    audio_thread_create(NULL, "brtc_demo_cam", brtc_demo_cam_task, NULL, 8 * 1024, 14, true, 1);
    AUDIO_MEM_SHOW(TAG);
    brtc_demo_video_pkg_queue = xQueueCreate(10, sizeof(brtc_demo_video_pkg_t));
    audio_thread_create(NULL, "brtc_demo_video_decoder_and_render", brtc_demo_video_decoder_and_render, NULL, 8 * 1024, 14, true, 0);
    AUDIO_MEM_SHOW(TAG);
#endif
}


