/* http client request example code

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>

#include "freertos/idf_additions.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_wifi.h"

#include "audio_sys.h"
#include "audio_thread.h"
#include "esp_peripherals.h"
#include "periph_wifi.h"
#include "periph_sdcard.h"
#include "audio_mem.h"
#include "board.h"
#include "es8311.h"
#include "es7210.h"

#include "audio_processor.h"
#include "coze_chat.h"

static char *TAG = "main";
struct coze_ws_s {
    coze_chat_handle_t      chat;
    audio_recorder_handle_t recorder;
    audio_player_handle_t   player;
    char                   *recorder_buffer;
#define DEFAULT_RAW_OPUS_BUFFER_SIZE (1024)
    char                   *opus_raw_buffer;
    int                     opus_raw_buffer_len;
    enum {
        PLAYBACK_STATE_IDLE,
        PLAYBACK_STATE_PLAYING,
    } player_state;
};

static struct coze_ws_s s_coze_ws;

static void audio_event_callback(coze_chat_event_t event, void *ctx)
{
    if (event == COZE_CHAT_EVENT_CHAT_SPEECH_STARTED) {
        ESP_LOGI(TAG, "chat start");
        s_coze_ws.player_state = PLAYBACK_STATE_IDLE;
    } else if (event == COZE_CHAT_EVENT_CHAT_SPEECH_STOPED) {
        ESP_LOGI(TAG, "chat stop");
        s_coze_ws.player_state = PLAYBACK_STATE_PLAYING;
    }
}

static void audio_data_callback(char *data, int len, void *ctx)
{
#define frame_length_prefix (2)
    if (len > s_coze_ws.opus_raw_buffer_len) {
        s_coze_ws.opus_raw_buffer_len = len + frame_length_prefix;
        s_coze_ws.opus_raw_buffer = audio_realloc(s_coze_ws.opus_raw_buffer, s_coze_ws.opus_raw_buffer_len);
    }
    ESP_LOGD(TAG, "data: %p, len: %d", data, len);
    s_coze_ws.opus_raw_buffer[0] = (len >> 8) & 0xFF;
    s_coze_ws.opus_raw_buffer[1] = len & 0xFF;
    memcpy(s_coze_ws.opus_raw_buffer + frame_length_prefix, data, len);
    len += frame_length_prefix;

    player_pipeline_write(s_coze_ws.player, s_coze_ws.opus_raw_buffer, len);
}

static void audio_if_open()
{
    s_coze_ws.recorder = recorder_pipeline_open();
    s_coze_ws.player = player_pipeline_open();
    recorder_pipeline_run(s_coze_ws.recorder);
    player_pipeline_run(s_coze_ws.player);
}

static void audio_data_read_task(void *pv)
{
#define recorder_buffer_size (640)
    s_coze_ws.recorder_buffer = malloc(recorder_buffer_size);

    while (1) {
        int r_len = recorder_pipeline_read(s_coze_ws.recorder, s_coze_ws.recorder_buffer, recorder_buffer_size);
        ESP_LOGD(TAG, "read len: %d", r_len);
        if (r_len > 0) {
            coze_chat_send_audio_data(s_coze_ws.chat, s_coze_ws.recorder_buffer, r_len);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    AUDIO_MEM_SHOW(TAG);
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());

    ESP_LOGI(TAG, "Initialize board peripherals");
    esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
    esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

    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);
#if CONFIG_ESP32_S3_SPOT_BOARD
    audio_hal_set_volume(board_handle->audio_hal, 90);
    es8311_set_mic_gain(ES8311_MIC_GAIN_0DB);
#endif

    s_coze_ws.opus_raw_buffer_len = DEFAULT_RAW_OPUS_BUFFER_SIZE;
    s_coze_ws.opus_raw_buffer = audio_malloc(s_coze_ws.opus_raw_buffer_len);
    // Initialize SD Card peripheral
    // audio_board_sdcard_init(set, SD_MODE_1_LINE);
    periph_wifi_cfg_t wifi_cfg = {
        .wifi_config.sta.ssid = CONFIG_ESP_WIFI_SSID,
        .wifi_config.sta.password = CONFIG_ESP_WIFI_PASSWORD,
    };
    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);

    coze_chat_config_t chat_config = COZE_CHAT_DEFAULT_CONFIG();
    chat_config.bot_id = CONFIG_BOT_ID;
    chat_config.access_token = CONFIG_ACCESS_TOKEN;
    chat_config.audio_callback = audio_data_callback;
    chat_config.event_callback = audio_event_callback;

    s_coze_ws.chat = coze_chat_init(&chat_config);
    coze_chat_start(s_coze_ws.chat);

    audio_if_open();
    audio_thread_create(NULL, "audio_data_read_task", audio_data_read_task, (void *)NULL, 1024 * 4, 12, true, 1);

    AUDIO_MEM_SHOW(TAG);
}
