#!/usr/bin/env python3

import os
import time
import re
import rclpy
from rclpy.node import Node
from std_msgs.msg import String, Bool
import dashscope
from dashscope.audio.tts import SpeechSynthesizer
import pyaudio

class VoiceTTSNode(Node):
    def __init__(self):
        super().__init__('voice_tts_node')
        
        # 初始化 DashScope API Key
        dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
        
        # 初始化 PyAudio 用于播放
        self.p = pyaudio.PyAudio()
        
        # 自动检测可用的输出设备
        output_device_index = None
        for i in range(self.p.get_device_count()):
            device_info = self.p.get_device_info_by_index(i)
            if device_info['maxOutputChannels'] > 0:
                # 优先选择带有echo-cancel的设备
                if 'echo-cancel' in device_info['name'].lower():
                    output_device_index = i
                    self.get_logger().info(f'选择echo-cancel输出设备: {device_info["name"]}')
                    break
                # 否则选择第一个可用的输出设备
                elif output_device_index is None:
                    output_device_index = i
                    self.get_logger().info(f'选择默认输出设备: {device_info["name"]}')
        
        if output_device_index is None:
            self.get_logger().error('未找到可用的输出设备')
            # 不退出，而是在需要播放时再处理错误
            self.output_stream = None
        else:
            self.output_stream = self.p.open(
                format=pyaudio.paInt16,
                channels=1,
                rate=48000,
                output=True,
                frames_per_buffer=1024,
                output_device_index=output_device_index
            )
        
        # 创建订阅者，接收 LLM 回复
        self.subscription = self.create_subscription(
            String,
            'llm_reply',
            self.listener_callback,
            10)
        
        # 创建播放状态发布者
        self.playback_status_publisher = self.create_publisher(Bool, 'tts_playback_status', 10)
        
        self.get_logger().info('Voice TTS Node 已启动，正在监听 /llm_reply...')

    def listener_callback(self, msg):
        text = msg.data
        if not text:
            return

        self.get_logger().info(f'收到回复信息，开始语音合成并播放: "{text}"')

        try:
            # 检查输出设备是否可用
            if self.output_stream is None:
                self.get_logger().error('输出设备不可用，跳过语音播放')
                self.publish_playback_status(False)
                return
            
            # 调用 DashScope 语音合成
            # 使用 pcm 格式直接播放，采样率设为 48000
            result = SpeechSynthesizer.call(
                model='sambert-zhichu-v1',
                text=text,
                sample_rate=48000,
                format='pcm'
            )

            if result.get_audio_data() is not None:
                audio_data = result.get_audio_data()
                self.get_logger().info(f'语音合成成功，音频大小: {len(audio_data)} 字节，正在播放...')
                
                # 发布播放开始状态
                self.publish_playback_status(True)
                
                # 播放音频
                self.output_stream.write(audio_data)
                
                # 计算音频播放时间（采样率48000，每个采样2字节，单声道）
                # 播放时间 = 数据字节数 / (采样率 * 每采样字节数 * 声道数)
                play_duration = len(audio_data) / (48000 * 2 * 1)
                self.get_logger().info(f'预计播放时间: {play_duration:.2f} 秒')
                
                # 等待音频播放完成
                time.sleep(play_duration + 1.0)  # 额外增加1秒缓冲，避免回音被ASR识别到回音
                
                # 发布播放完成状态
                self.publish_playback_status(False)
            else:
                self.get_logger().error(f'语音合成失败: {result.get_response()}')
                self.publish_playback_status(False)

        except Exception as e:
            self.get_logger().error(f'语音合成或播放过程中发生错误: {str(e)}')
            # 确保在出错时也发布播放完成状态
            self.publish_playback_status(False)

    def publish_playback_status(self, is_playing):
        """发布播放状态"""
        msg = Bool()
        msg.data = is_playing
        self.playback_status_publisher.publish(msg)
        self.get_logger().info(f'发布TTS播放状态: {"播放中" if is_playing else "空闲"}')

    def destroy_node(self):
        # 释放资源
        if self.output_stream:
            try:
                self.output_stream.stop_stream()
                self.output_stream.close()
            except Exception as e:
                self.get_logger().error(f'关闭输出流时发生错误: {str(e)}')
        if self.p:
            try:
                self.p.terminate()
            except Exception as e:
                self.get_logger().error(f'终止PyAudio时发生错误: {str(e)}')
        super().destroy_node()

def main(args=None):
    rclpy.init(args=args)
    node = VoiceTTSNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()
