#include "common.h"
#include "audioplay.h"
#include "audio_sink.h"  /* USB声卡排队锁 */
#include "device_identity.h"
#include "mqtt_init.h"
#include "http_config_mqtt.h"
#include "audiotts_play.h"
#include "wifi_autoconfig.h"
#include <stdio.h>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>

#define AUDIO_USB_ALSA_DEVICE "hw:2,0"
#define AUDIO_LOCAL_ALSA_DEVICE "plughw:2,0"
#define AUDIO_ANNOUNCEMENT_TIMEOUT_SEC 20

static int s_audio_status=7;
static char s_urlbuf[512];
static char s_keybuf[256];
static double s_audio_volume=0.8;

#define AUDIO_DRIVING_LEVEL_MAX 30

static int s_local_play_pending = 0;
static char s_local_filepath[512];
static pthread_mutex_t s_local_play_mutex = PTHREAD_MUTEX_INITIALIZER;

static int local_is_cn_lang(const char *lang) {
    return lang != NULL && (strcmp(lang, AUDIO_LANG_ZH) == 0 || strcmp(lang, "cn") == 0);
}

static int local_is_en_lang(const char *lang) {
    return lang != NULL &&
           (strcmp(lang, AUDIO_LANG_EN) == 0 || strcmp(lang, "en") == 0 || strcmp(lang, "EN") == 0);
}

static void local_resolve_lang(const char *language_override, char *out, size_t size) {
    if (language_override == NULL || language_override[0] == '\0' ||
        strcasecmp(language_override, "default") == 0) {
        snprintf(out, size, "%s", audio_get_language());
        return;
    }
    if (local_is_cn_lang(language_override)) {
        snprintf(out, size, "%s", AUDIO_LANG_ZH);
        return;
    }
    if (local_is_en_lang(language_override)) {
        snprintf(out, size, "%s", AUDIO_LANG_EN);
        return;
    }
    snprintf(out, size, "%s", audio_get_language());
    my_zlog_warn("2017 未知 language: %s，使用当前语言: %s", language_override, out);
}

static int local_resolve_filepath(const char *filename, const char *language_override,
                                  char *out, size_t size) {
    char lang[8];
    int use_default_lang = (language_override == NULL || language_override[0] == '\0' ||
                            strcasecmp(language_override, "default") == 0);

    local_resolve_lang(language_override, lang, sizeof(lang));

    if (local_is_cn_lang(lang)) {
        snprintf(out, size, "%s/%s", AUDIO_LOCAL_BASE, filename);
        return access(out, F_OK) == 0;
    }

    snprintf(out, size, "%s/%s/%s", AUDIO_LOCAL_BASE, lang, filename);
    if (access(out, F_OK) == 0) {
        return 1;
    }
    if (use_default_lang) {
        snprintf(out, size, "%s/%s", AUDIO_LOCAL_BASE, filename);
        return access(out, F_OK) == 0;
    }
    return 0;
}

static void local_queue_play(const char *filename, const char *language) {
    char resolved[512];
    if (!local_resolve_filepath(filename, language, resolved, sizeof(resolved))) {
        my_zlog_warn("2017 本地音频不存在: %s", filename);
        return;
    }
    pthread_mutex_lock(&s_local_play_mutex);
    snprintf(s_local_filepath, sizeof(s_local_filepath), "%s", resolved);
    s_local_play_pending = 1;
    pthread_mutex_unlock(&s_local_play_mutex);
    my_zlog_info("2017 已排队本地音频: %s", resolved);
}

void audioplay_local_mqtt_receive(cJSON *body) {
    if (!cJSON_IsObject(body)) {
        my_zlog_warn("2017 body 无效");
        return;
    }

    cJSON *status = cJSON_GetObjectItemCaseSensitive(body, "status");
    cJSON *level_item = cJSON_GetObjectItemCaseSensitive(body, "level");
    cJSON *language = cJSON_GetObjectItemCaseSensitive(body, "language");

    if (!cJSON_IsString(status) || status->valuestring == NULL) {
        my_zlog_warn("2017 缺少 status");
        return;
    }

    const char *lang = "default";
    if (cJSON_IsString(language) && language->valuestring != NULL) {
        lang = language->valuestring;
    }

    if (strcmp(status->valuestring, "speed") == 0) {
        local_queue_play("Speedupforoneminute.mp3", lang);
        return;
    }

    if (strcmp(status->valuestring, "drivinglevel") == 0) {
        int level = 0;
        if (cJSON_IsString(level_item) && level_item->valuestring != NULL) {
            level = atoi(level_item->valuestring);
        } else if (cJSON_IsNumber(level_item)) {
            level = level_item->valueint;
        } else {
            my_zlog_warn("2017 drivinglevel 缺少 level");
            return;
        }
        if (level < 1 || level > AUDIO_DRIVING_LEVEL_MAX) {
            my_zlog_warn("2017 drivinglevel level 无效: %d (1-%d)",
                         level, AUDIO_DRIVING_LEVEL_MAX);
            return;
        }
        char filename[48];
        snprintf(filename, sizeof(filename), "DrivingLevel%d.mp3", level);
        local_queue_play(filename, lang);
        return;
    }

    my_zlog_warn("2017 未知 status: %s", status->valuestring);
}

static double audioplay_volume_clamp(double v) {
	if (v < 0.0) {
		return 0.0;
	}
	if (v > 2.0) {
		return 2.0;
	}
	return v;
}

int audio_wheat_init();

static int audio_system_exit_code(int status) {
    if (status == -1) {
        return -1;
    }
    if (WIFEXITED(status)) {
        return WEXITSTATUS(status);
    }
    if (WIFSIGNALED(status)) {
        return 128 + WTERMSIG(status);
    }
    return -1;
}

static void shell_single_quote(char *out, size_t size, const char *in) {
    size_t pos = 0;
    if (!out || size == 0) {
        return;
    }
    out[pos++] = '\'';
    if (in) {
        for (const char *p = in; *p && pos + 5 < size; p++) {
            if (*p == '\'') {
                const char *esc = "'\\''";
                for (const char *e = esc; *e && pos + 1 < size; e++) {
                    out[pos++] = *e;
                }
            } else {
                out[pos++] = *p;
            }
        }
    }
    if (pos + 1 < size) {
        out[pos++] = '\'';
    }
    out[pos] = '\0';
}

static int pulse_suspend_default_output(int suspend) {
    const char *command = suspend
        ? "pactl suspend-sink @DEFAULT_SINK@ 1 >/dev/null 2>&1"
        : "pactl suspend-sink @DEFAULT_SINK@ 0 >/dev/null 2>&1";
    int exit_code = audio_system_exit_code(system(command));

    if (exit_code != 0) {
        my_zlog_warn("%s浏览器音频输出失败 exit=%d",
                     suspend ? "暂停" : "恢复", exit_code);
    }
    return exit_code;
}

int audioplay_file_with_browser_preempt(const char *filepath, double volume) {
    char quoted_path[1024];
    char command[2048];
    int ret;
    int exit_code;
    int pulse_suspended;

    if (filepath == NULL || filepath[0] == '\0') {
        return -1;
    }

    shell_single_quote(quoted_path, sizeof(quoted_path), filepath);
    volume = audioplay_volume_clamp(volume);

    /* 先和项目内 DataChannel/本地音频排队，再让浏览器临时释放 USB 声卡。 */
    audio_sink_lock_alsa();
    pulse_suspended = (pulse_suspend_default_output(1) == 0);

    if (pulse_suspended) {
        snprintf(command, sizeof(command),
                 /* timeout 使用 KILL，保证卡死的播放进程到 20 秒时立即释放 ALSA。 */
                 "timeout -s KILL %ds gst-launch-1.0 -q filesrc location=%s ! mpegaudioparse ! mpg123audiodec ! audioconvert ! volume volume=%.3f ! audioresample ! audio/x-raw,channels=2 ! alsasink device=%s sync=true >/dev/null 2>&1",
                 AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, quoted_path, volume,
                 AUDIO_LOCAL_ALSA_DEVICE);
    } else {
        /* PulseAudio 无法暂停时走其默认输出，避免直接打开同一声卡导致 device busy。 */
        snprintf(command, sizeof(command),
                 "timeout -s KILL %ds ffplay -nodisp -autoexit -loglevel warning -af \"volume=%.3f\" %s >/dev/null 2>&1",
                 AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, volume, quoted_path);
    }

    ret = system(command);
    exit_code = audio_system_exit_code(ret);

    /* 无论正常结束、播放失败还是 timeout，退出前都恢复浏览器音频。 */
    if (pulse_suspended) {
        pulse_suspend_default_output(0);
    }
    audio_sink_unlock_alsa();

    if (exit_code == 137) {
        my_zlog_warn("播报超过 %d 秒，已强制停止并释放声卡: %s",
                     AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, filepath);
    }
    return exit_code;
}

int audioplay_url_with_browser_preempt(const char *url, double volume) {
    char quoted_url[1024];
    char command[2048];
    int exit_code;
    int pulse_suspended;

    if (url == NULL || url[0] == '\0') {
        return -1;
    }

    shell_single_quote(quoted_url, sizeof(quoted_url), url);
    volume = audioplay_volume_clamp(volume);

    audio_sink_lock_alsa();
    pulse_suspended = (pulse_suspend_default_output(1) == 0);

    if (pulse_suspended) {
        snprintf(command, sizeof(command),
                 "timeout -s KILL %ds ffmpeg -nostdin -hide_banner -loglevel warning -i %s -af \"volume=%.3f\" -f alsa %s >/dev/null 2>&1",
                 AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, quoted_url, volume,
                 AUDIO_LOCAL_ALSA_DEVICE);
    } else {
        snprintf(command, sizeof(command),
                 "timeout -s KILL %ds ffplay -nodisp -autoexit -loglevel warning -af \"volume=%.3f\" %s >/dev/null 2>&1",
                 AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, volume, quoted_url);
    }

    exit_code = audio_system_exit_code(system(command));

    if (pulse_suspended) {
        pulse_suspend_default_output(0);
    }
    audio_sink_unlock_alsa();

    if (exit_code == 137) {
        my_zlog_warn("网络播报超过 %d 秒，已强制停止并释放声卡: %s",
                     AUDIO_ANNOUNCEMENT_TIMEOUT_SEC, url);
    }
    return exit_code;
}

//接收音频播放
void audioplay_mqtt_receive(cJSON *json) {
    // 解析"audioLink"字段（修正了原始JSON中的拼写错误）
    cJSON *audio_link = cJSON_GetObjectItemCaseSensitive(json, "audioLink");
    if (cJSON_IsString(audio_link) && (audio_link->valuestring != NULL)) {
        my_zlog_debug("音频链接: %s", audio_link->valuestring);
        char *url=audio_link->valuestring;
        snprintf(s_urlbuf, sizeof(s_urlbuf), "%s", url);
    } else {
        my_zlog_warn("错误：无法解析音频链接字段");
    }

    // 解析"key"字段
    cJSON *key_char = cJSON_GetObjectItemCaseSensitive(json, "key");
    if (cJSON_IsString(key_char) && (key_char->valuestring != NULL)) {
        my_zlog_debug("音频链接KEY: %s", key_char->valuestring);
        char *key=key_char->valuestring;
        snprintf(s_keybuf, sizeof(s_keybuf), "%s", key);
    } else {
        my_zlog_warn("错误：无法解析音频链接");
        s_audio_status=5;
    }

    // 解析"status"字段
    cJSON *s_audio_status_val = cJSON_GetObjectItemCaseSensitive(json, "status");
    if (cJSON_IsNumber(s_audio_status_val)) {
        my_zlog_debug("标志: %d", s_audio_status_val->valueint);
        s_audio_status=s_audio_status_val->valueint;
    } else {
        my_zlog_warn("错误：无法解析标志字段");
    }

    // 解析"volume"字段
    cJSON *volume = cJSON_GetObjectItemCaseSensitive(json, "volume");
    if (cJSON_IsNumber(volume)) {
        my_zlog_debug("声量大小: %.3f", volume->valuedouble);
        s_audio_volume = audioplay_volume_clamp(volume->valuedouble);
    } else {
        my_zlog_warn("错误：无法解析声量字段");
    }
}  

//发送音频播放是否完毕
void  audioplay_send_mqtt() {
    cJSON *root = cJSON_CreateObject();
    cJSON *body = cJSON_CreateObject();
    cJSON *head = cJSON_CreateObject();
        // 添加各个字段到 JSON 对象
    cJSON_AddStringToObject(body, "type", "audio");
    cJSON_AddStringToObject(body, "audioLink", s_urlbuf);
    cJSON_AddStringToObject(body, "key", s_keybuf);
    cJSON_AddNumberToObject(body, "status", s_audio_status);
    cJSON_AddNumberToObject(body, "volume", s_audio_volume);

    cJSON_AddNumberToObject(head, "message_type",3001);

    cJSON_AddItemToObject(root, "body", body);
    cJSON_AddItemToObject(root, "head",head);
    // 将 JSON 对象转换为字符串
    char* json_string = cJSON_PrintUnformatted(root);
    my_zlog_debug("%s",json_string);

    mqtt_publish_to_all(mqtt_topic_pure_number(), json_string, 0);
    free(json_string);
    cJSON_Delete(root);
}

//音频播放
void audioplay_cycle(){
    int ret;
    while(1){
        if(s_audio_status==0){

            char *urlmoddle=s_urlbuf;
            s_audio_volume = audioplay_volume_clamp(s_audio_volume);

            my_zlog_debug("播放地址: %s", s_urlbuf);
            ret = audioplay_url_with_browser_preempt(urlmoddle, s_audio_volume);
            if (ret != 0) {
                my_zlog_error("播放失败");
                s_audio_status=2;
            }

            if (ret == 0) {
                my_zlog_debug("播放已成功完成 : %s ", s_urlbuf);  
                s_audio_status=1;   
            } else {
                my_zlog_warn("播放失败或中断: %s ", s_urlbuf);
                s_audio_status=2;
            }
        audioplay_send_mqtt();
        }

        char local_filepath[sizeof(s_local_filepath)];
        int local_play_pending = 0;

        pthread_mutex_lock(&s_local_play_mutex);
        if (s_local_play_pending) {
            s_local_play_pending = 0;
            snprintf(local_filepath, sizeof(local_filepath), "%s", s_local_filepath);
            local_play_pending = 1;
        }
        pthread_mutex_unlock(&s_local_play_mutex);

        if (local_play_pending) {
            int exit_code;
            my_zlog_info("播放本地音频: %s", local_filepath);
            exit_code = audioplay_file_with_browser_preempt(local_filepath, 1.0);
            if (exit_code == 0) {
                my_zlog_debug("本地音频播放完成: %s", local_filepath);
            } else {
                my_zlog_warn("本地音频播放失败 exit=%d: %s", exit_code, local_filepath);
            }
        }

        video_tts_play();

        delay_ms(100);
    
    }
}

static void trim_line(char *s)
{
    if (!s)
        return;
    s[strcspn(s, "\r\n")] = '\0';
}

static int pulse_pactl_exit_code(int status)
{
    if (status == -1)
        return -1;
    if (WIFEXITED(status))
        return WEXITSTATUS(status);
    return -1;
}

static int pulse_output_means_already_loaded(const char *output)
{
    if (!output || !output[0])
        return 0;
    return strstr(output, "already loaded") != NULL ||
           strstr(output, "Already loaded") != NULL ||
           strstr(output, "Module exists") != NULL;
}

static int pulse_module_exists(const char *module_name, const char *device)
{
    FILE *fp = popen("pactl list modules short 2>/dev/null", "r");
    char line[512];
    char needle[96];

    if (!fp)
        return 0;

    snprintf(needle, sizeof(needle), "device=%s", device);
    while (fgets(line, sizeof(line), fp) != NULL) {
        if (strstr(line, module_name) != NULL && strstr(line, needle) != NULL) {
            pclose(fp);
            return 1;
        }
    }

    pclose(fp);
    return 0;
}

static int pulse_load_alsa_module(const char *module_name, const char *label)
{
    char cmd[256];
    char output[512];
    char line[256];

    if (pulse_module_exists(module_name, AUDIO_USB_ALSA_DEVICE)) {
        my_zlog_info("%s 已注册，跳过 (%s device=%s)",
                     label, module_name, AUDIO_USB_ALSA_DEVICE);
        return 0;
    }

    snprintf(cmd, sizeof(cmd), "pactl load-module %s device=%s 2>&1",
             module_name, AUDIO_USB_ALSA_DEVICE);

    FILE *fp = popen(cmd, "r");
    if (!fp) {
        my_zlog_warn("%s 注册失败: 无法执行 pactl (%s)", label, strerror(errno));
        return -1;
    }

    output[0] = '\0';
    while (fgets(line, sizeof(line), fp) != NULL) {
        trim_line(line);
        if (!line[0])
            continue;
        if (output[0]) {
            strncat(output, "; ", sizeof(output) - strlen(output) - 1);
        }
        strncat(output, line, sizeof(output) - strlen(output) - 1);
    }

    int status = pclose(fp);
    int exit_code = pulse_pactl_exit_code(status);

    if (exit_code == 0) {
        my_zlog_info("%s 注册成功 (%s device=%s)%s%s",
                     label, module_name, AUDIO_USB_ALSA_DEVICE,
                     output[0] ? ", pactl: " : "", output[0] ? output : "");
        return 0;
    }

    if (pulse_output_means_already_loaded(output) ||
        pulse_module_exists(module_name, AUDIO_USB_ALSA_DEVICE)) {
        my_zlog_info("%s 已注册，跳过 (%s device=%s)%s%s",
                     label, module_name, AUDIO_USB_ALSA_DEVICE,
                     output[0] ? ": " : "", output[0] ? output : "");
        return 0;
    }

    my_zlog_warn("%s 注册失败 (%s device=%s): exit=%d%s%s",
                 label, module_name, AUDIO_USB_ALSA_DEVICE, exit_code,
                 output[0] ? ", pactl: " : ", pactl 无输出",
                 output[0] ? output : "");
    return -1;
}

int audio_wheat_init(){
    return pulse_load_alsa_module("module-alsa-sink", "喇叭");
}

int audio_speaker_init() {
    return pulse_load_alsa_module("module-alsa-source", "麦");
}

int audio_init(){

    delay_s(1);
    audio_wheat_init();
    delay_s(1);
    audio_speaker_init();

    return 0;
}


#define CONFIG_FILE "/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf"

static const char *s_yundea_block =
"# 为 Yundea 1076 USB 二合一设备添加配置\n"
"[Mapping yundea-input]\n"
"device-strings = hw:%f,0\n"
"direction = input\n"
"priority = 100\n"
"channel-map = mono\n"
"\n"
"[Mapping yundea-output]\n"
"device-strings = hw:%f,0\n"
"direction = output\n"
"priority = 100\n"
"channel-map = left,right\n"
"\n"
"[Profile yundea-duplex]\n"
"input-mappings = yundea-input\n"
"output-mappings = yundea-output\n"
"priority = 200\n";

/*
*功能：加入usb声卡配置
*
*/
int audio_config_init() {

    delay_s(12);

    FILE *fp = fopen(CONFIG_FILE, "r");
    if (!fp) {
        perror("无法打开配置文件进行读取");
        return 1;
    }

    // 获取文件大小
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char *buffer = malloc(size + 1);
    if (!buffer) {
        my_zlog_error("内存分配失败");
        fclose(fp);
        return 1;
    }

    size_t result =fread(buffer, 1, size, fp);
    if (result != size) {
    my_zlog_warn("Error: Failed to read audio data");
    }
    buffer[size] = '\0';
    fclose(fp);

    // 检查是否包含完整配置块
    if (strstr(buffer, s_yundea_block) != NULL) {
        my_zlog_debug("配置文件中已包含指定配置，跳过添加。");
        free(buffer);
        return 0;
    }

    free(buffer);

   my_zlog_info("未找到指定配置，正在追加到文件末尾...");

    // 打开文件追加配置
    fp = fopen(CONFIG_FILE, "a");
    if (!fp) {
        my_zlog_error("无法打开配置文件进行写入");
        return 1;
    }

    fprintf(fp, "\n%s", s_yundea_block);
    fclose(fp);

    my_zlog_info("配置已成功追加。");
    return 0;
}
