#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
//#include "video_raw_data.h"

#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

#ifdef BRTC_DEMO_VIDEO
#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"
static esp_lcd_panel_handle_t panel_handle = NULL;
static bool g_enable_visual = true;                  //true:开启视觉互动模式
static int  g_vision_mode = VISION_MODE_STREAM;      //开启视频流理解模式
#endif
#include "esp_log.h"
//==============================================================================
// General Definitions and Global Variables
//==============================================================================
#define BRTC_LOG printf
//#define VIDEO_NAL_DATA jpeg_640x480_frame
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;

/******************************************************************************
 ******************************************************************************
 **
 ** Wi-Fi Section
 **
 ******************************************************************************
 ******************************************************************************/
#define BRTC_WIFI_SSID      "BD"
#define BRTC_WIFI_PASS      "11111111"
//#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);

    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);
}
#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
 **
 ******************************************************************************
 ******************************************************************************/
const char* TAG = "main";
#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

int brtc_demo_jpeg_dec(unsigned char **pixels, const unsigned char *data, size_t len, int* width, int *height)
{
    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;
        // 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_LOGD(TAG, "jpeg_dec_image:%d (%d * %d)", outbuf_len, out_info.width, out_info.height);
    unsigned char *out_buf = jpeg_calloc_align(outbuf_len, 16);
    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;
    return ESP_OK;
}

static void brtc_demo_lcd_draw(esp_lcd_panel_handle_t panel_handle, uint8_t *image) {
    uint32_t lines_num = 40;
    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);
        }
        jpeg_free_align(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) {
            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;

    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 (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 OnVideoFrame(int64_t feedid, const char *img, int len, RtcImageType imgtype, int width, int height){
    if (!brtcDemoRunning) {
        return;
    }
    //trace_size("VRecv", len);
/*
#ifdef BRTC_DEMO_VIDEO
    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:
            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, bool final) {
    BRTC_LOG("User ASR subtitle: %s (final: %d)\n", text, 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);
        }
    }
#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) {
    //BRTC_LOG("onAIAgentSpeaking.%d\n", speeking);
    BRTC_LOG("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, int width, int height) {
    BRTC_LOG("Received video data of length: %zu, width: %d, height: %d\n", len, width, height);
}

void onLicenseResult (bool result) {
    printf("onLicenseResult: %d\n", result);
    if (!result) {
        BRTC_LOG("onLicenseResult: failed\n");
        g_brtc_license_abnormal_exit = true;
    }
}

void onVisionImageRequest(void) {
}

void onVisionImageAck(const char* name) {
}

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\\\", \\\"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", "xxxx");
    snprintf(params->config, sizeof(params->config), "%s", config);
    printf("params->config:%s\n", params->config);
    params->instance_id = 10373;
    params->verbose = false;
    params->enable_local_agent =true;
    params->enable_voice_interrupt = true;
    params->disable_send_mute_data = true;
    params->level_voice_interrupt = 80;
    params->AudioInChannel = 1;
    params->AudioInFrequency = 16000;
    params->region = REGION_MAINLAND; //国内：REGION_MAINLAND 国外：REGION_OVERSEAS
#ifdef BRTC_DEMO_VIDEO
    params->enable_video = g_enable_visual; // 视觉理解场景是否开启
#endif
}

void brtc_demo_init(void) {
    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,
        .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;

    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);
}

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
/******************************************************************************
 ******************************************************************************
 **
 ** 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;
    }

    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);
    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);

    //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);
#endif
}


