<template>
    <div class="audio-spectrum">
        <el-scrollbar height="680px">
            <!-- 分贝图表容器 -->
            <div ref="dbChartContainer" class="spectrum-chart-db"></div>
            <!-- 频率图表容器 -->
            <div ref="chartContainer" class="spectrum-chart"></div>
        </el-scrollbar>
        
        <!-- 音频播放器 -->
        <audio ref="audioElement" style="width: 100%;" :src="fileUrl" controls></audio>
    </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import * as echarts from 'echarts';

// ==================== 常量定义 ====================
const COLORS = {
    PRIMARY: '#63e2b7',
    SECONDARY: '#8b9cff',
    TERTIARY: '#ff88d0',
    DB_CHART: '#ff6666',
    AXIS: '#717d95',
    TEXT: '#8c94a3'
};

// ==================== props定义 ====================
const props = defineProps({
    fileUrl: {
        type: String,
        required: true
    }
});

// ==================== DOM引用 ====================
const chartContainer = ref(null);
const dbChartContainer = ref(null);
const audioElement = ref(null);

// ==================== 图表实例 ====================
let chartInstance = null;
let dbChartInstance = null;

// ==================== 音频分析类 ====================
/**
 * 音频分析器类
 * 负责音频数据采集、分析和图表数据更新
 */
class AudioAnalyzer {
    static FFT_SIZE = 8192;
    static SAMPLE_INTERVAL = 100;

    /**
     * 初始化音频分析器
     */
    constructor() {
        // 频率分析数据 [基频, 第一共振峰, 第二共振峰]
        this.chartData = [[], [], []];  
        // 分贝数据 [频率-Hz, 强度-dB]
        this.dbChartData = [];          
        // 创建音频上下文
        this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
        // 创建分析器节点
        this.analyser = this.audioContext.createAnalyser();
        this.analyser.fftSize = AudioAnalyzer.FFT_SIZE;
        this.analyser.smoothingTimeConstant = 0.7;
        // 初始化分析数据缓冲区
        this.dataArray = new Float32Array(this.analyser.frequencyBinCount);
        // 动画帧ID
        this.animationId = null;
        // 停止标志
        this.isStopped = false;
    }

    /**
     * 连接媒体源到分析器
     * @param {HTMLMediaElement} mediaElement - 音频元素
     */
    connectMediaSource(mediaElement) {
        const mediaSource = this.audioContext.createMediaElementSource(mediaElement);
        mediaSource.connect(this.analyser);
        this.analyser.connect(this.audioContext.destination);
    }

    /**
     * 开始音频分析
     */
    startAnalyze() {
        if (this.animationId) return;
        this._analyze();
    }

    /**
     * 停止音频分析
     */
    stopAnalyze() {
        if (this.animationId) {
            cancelAnimationFrame(this.animationId);
            this.animationId = null;
        }
        this.isStopped = true;
    }

    /**
     * 音频分析循环
     */
    _analyze() {
        if (this.isStopped) return;
        
        // 获取当前频率数据
        this.analyser.getFloatFrequencyData(this.dataArray);
        // 分析共振峰
        const peaks = this._analyzeFormants();
        // 更新图表数据
        this._updateChart(peaks);

        // 使用requestAnimationFrame配合setTimeout控制采样间隔
        this.animationId = requestAnimationFrame(() => {
            setTimeout(() => {
                if (!this.isStopped) this._analyze();
            }, AudioAnalyzer.SAMPLE_INTERVAL);
        });
    }

    /**
     * 分析共振峰数据
     * @returns {Array} 共振峰数据数组
     */
    _analyzeFormants() {
        const sampleRate = this.audioContext.sampleRate;
        const linearData = this.dataArray.map(v => 10 ** (v / 20)); // 线性幅度转换
        const dbData = this.dataArray.slice(); // 原始分贝值
        
        // 更新分贝图表数据
        this.dbChartData = Array.from(dbData).map((db, index) => [
            index * sampleRate / AudioAnalyzer.FFT_SIZE,
            db
        ]);
        
        // 查找三个频段的主峰
        return [
            this._findDominantPeak(linearData, sampleRate, 50, 300),
            this._findDominantPeak(linearData, sampleRate, 300, 1000),
            this._findDominantPeak(linearData, sampleRate, 1000, 3000)
        ].filter(Boolean);
    }

    /**
     * 查找指定频段的主峰
     * @param {Array} data - 音频数据
     * @param {number} sampleRate - 采样率
     * @param {number} lowFreq - 最低频率
     * @param {number} highFreq - 最高频率
     * @returns {Object|null} 主峰信息
     */
    _findDominantPeak(data, sampleRate, lowFreq, highFreq) {
        const minBin = Math.floor(lowFreq * AudioAnalyzer.FFT_SIZE / sampleRate);
        const maxBin = Math.ceil(highFreq * AudioAnalyzer.FFT_SIZE / sampleRate);

        let maxIndex = -1;
        let maxValue = -Infinity;

        for (let i = minBin; i <= maxBin; i++) {
            if (data[i] > maxValue) {
                maxValue = data[i];
                maxIndex = i;
            }
        }

        return maxIndex !== -1 ? {
            frequency: maxIndex * sampleRate / AudioAnalyzer.FFT_SIZE,
            amplitude: maxValue
        } : null;
    }

    /**
     * 更新图表数据
     * @param {Array} peaks - 共振峰数据
     */
    _updateChart(peaks) {
        const currentTime = audioElement.value?.currentTime || 0;
        if (currentTime <= 0) return;

        const msTime = currentTime * 1000;
        peaks.forEach((peak, i) => {
            if (i < 3 && peak) {
                this.chartData[i] = [...this.chartData[i], [msTime, peak.frequency]];
            }
        });
        this.updateChartSeries();
        updatePlayhead(msTime);
    }

    /**
     * 更新图表系列数据
     */
    updateChartSeries() {
        const option = chartInstance.getOption();
        // 使用slice替代JSON深拷贝，提升性能
        this.chartData.forEach((d, i) => {
            option.series[i].data = d.slice();
        });
        chartInstance.setOption(option);

        // 同步更新分贝图表
        dbChartInstance.setOption({
            series: [{ data: this.dbChartData }]
        });
    }
}

// ==================== 图表配置 ====================
/**
 * 初始化图表
 */
const initChart = () => {
    // 创建主图表实例
    chartInstance = echarts.init(chartContainer.value);
    // 创建分贝图表实例
    dbChartInstance = echarts.init(dbChartContainer.value);
    
    // 延迟调整尺寸以确保正确渲染
    setTimeout(() => {
        chartInstance.resize();
        dbChartInstance.resize();
    }, 10);

    // 深色主题配置
    const darkTheme = {
        backgroundColor: 'rgb(0 30 85)',
    };

    // 主图表配置
    const mainChartOption = {
        ...darkTheme,
        title: { 
            text: '音频频谱分析', 
            left: 'center', 
            textStyle: { color: '#ffffff' } 
        },
        tooltip: {
            trigger: 'axis',
            formatter: params => `时间: ${params[0].value[0].toFixed(0)}ms<br>${params.map(p => 
                `${p.marker} ${p.seriesName}: ${p.value[1].toFixed(1)}Hz`
            ).join('<br>')}`
        },
        legend: { 
            data: ['基频', '第一共振峰', '第二共振峰'], 
            top: 35, 
            textStyle: { color: '#e4e7ed' } 
        },
        xAxis: {
            type: 'value',
            name: '时间 (ms)',
            axisLabel: { formatter: value => `${Math.round(value)}ms` },
            axisLine: { lineStyle: { color: COLORS.AXIS } },
            axisLabel: { color: COLORS.TEXT }
        },
        yAxis: {
            type: 'log',
            name: '频率 (Hz)',
            min: 50,
            max: 5000,
            axisLabel: { 
                formatter: value => value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value 
            },
            axisLine: { lineStyle: { color: COLORS.AXIS } },
            axisLabel: { color: COLORS.TEXT }
        },
        series: [
            createSeriesOption('基频', COLORS.PRIMARY, 2),
            createSeriesOption('第一共振峰', COLORS.SECONDARY, 1.5),
            createSeriesOption('第二共振峰', COLORS.TERTIARY, 1.5)
        ],
        dataZoom: [{ type: 'inside' }],
        grid: { top: 80, bottom: 80 },
        graphic: [{
            type: 'line',
            z: 100,
            invisible: true,
            shape: { y1: 0, y2: 1 },
            style: { stroke: '#ff4d4f', lineWidth: 2 }
        }]
    };

    // 分贝图表配置
    const dbChartOption = {
        ...darkTheme,
        title: { 
            text: '频谱强度（分贝）', 
            left: 'center', 
            textStyle: { color: '#ffffff' } 
        },
        tooltip: {
            trigger: 'axis',
            formatter: params => `频率: ${params[0].axisValue}Hz<br>强度: ${params[0].value}dB`
        },
        xAxis: {
            type: 'log',
            name: '频率 (Hz)',
            min: 400,
            max: 5000,
            axisLabel: { 
                formatter: value => value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value 
            },
            axisLine: { lineStyle: { color: COLORS.AXIS } },
            axisLabel: { color: COLORS.TEXT }
        },
        yAxis: {
            type: 'value',
            name: '强度 (dB)',
            axisLine: { lineStyle: { color: COLORS.AXIS } },
            axisLabel: { color: COLORS.TEXT }
        },
        series: [{
            name: '频谱强度',
            type: 'line',
            data: [],
            showSymbol: false,
            lineStyle: { 
                color: COLORS.DB_CHART, 
                width: 1.5, 
                shadowColor: COLORS.DB_CHART, 
                shadowBlur: 8 
            }
        }],
        grid: { top: 80, bottom: 80 }
    };

    // 应用图表配置
    chartInstance.setOption(mainChartOption);
    dbChartInstance.setOption(dbChartOption);
};

/**
 * 创建系列配置
 * @param {string} name - 系列名称
 * @param {string} color - 系列颜色
 * @param {number} width - 线宽
 * @returns {Object} 系列配置对象
 */
const createSeriesOption = (name, color, width) => ({
    name,
    type: 'line',
    smooth: true,
    showSymbol: false,
    lineStyle: { 
        color, 
        width, 
        shadowColor: color, 
        shadowBlur: 8 
    }
});

/**
 * 更新播放头位置
 * @param {number} currentTime - 当前时间（毫秒）
 */
const updatePlayhead = (currentTime) => {
    chartInstance.setOption({
        graphic: [{
            invisible: false,
            position: [currentTime, 0],
            shape: {
                x1: currentTime,
                x2: currentTime,
                y1: 0,
                y2: chartInstance.getHeight()
            }
        }]
    });
};

// ==================== 主逻辑 ====================
let analyzer = null;

/**
 * 播放事件处理函数
 */
const playHandler = () => {
    try {
        analyzer.connectMediaSource(audioElement.value);
        analyzer.startAnalyze();
    } catch (error) {
        console.error('音频连接失败:', error);
    }
};

onMounted(() => {
    initChart();
    analyzer = new AudioAnalyzer();

    // 添加事件监听器
    audioElement.value.addEventListener('play', playHandler);
    audioElement.value.addEventListener('pause', () => {
        analyzer.stopAnalyze();
    });
    audioElement.value.addEventListener('ended', () => {
        analyzer.stopAnalyze();
    });
});

onBeforeUnmount(() => {
    // 清理资源
    analyzer?.stopAnalyze();
    analyzer?.audioContext?.close();
    chartInstance?.dispose();
    dbChartInstance?.dispose();
});

/**
 * 文件URL变更监听器
 */
watch(() => props.fileUrl, async () => {
    try {
        // 停止当前分析
        if (analyzer) {
            analyzer.stopAnalyze();
            await analyzer.audioContext.close();
        }

        // 重置图表
        chartInstance.setOption({
            series: [{ data: [] }, { data: [] }, { data: [] }],
            graphic: [{ invisible: true }]
        }, true);

        // 等待DOM更新
        await new Promise(resolve => setTimeout(resolve, 50));
        
        // 创建新分析器
        analyzer = new AudioAnalyzer();
        audioElement.value.load();

        // 重新绑定事件监听器
        audioElement.value.removeEventListener('play', playHandler);
        audioElement.value.addEventListener('play', playHandler);
    } catch (error) {
        console.error('文件加载失败:', error);
    }
});
</script>

<style scoped>
.audio-spectrum {
    font-family: Arial;
    padding: 20px;
}

.spectrum-chart,
.spectrum-chart-db {
    width: 100%;
    height: 320px;
    background: rgba(255, 255, 255, 0.144);
    border-radius: 8px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
}

audio {
    width: 100%;
    margin-top: 20px;
    border-radius: 8px;
    padding: 8px;
}

/* 音频控件样式优化 */
audio::-webkit-media-controls-panel {
    background: #008eaad7;
}

audio::-webkit-media-controls-play-button,
audio::-webkit-media-controls-mute-button {
    filter: invert(0.9);
}

audio::-webkit-media-controls-current-time-display,
audio::-webkit-media-controls-time-remaining-display {
    color: #fff;
}
/* 轨道 */
audio::-webkit-media-controls-timeline::before {
    content: '';
    display: block;
    height: 6px;
    background-color: #ff0000; /* 已播放部分颜色 */
}

/* 滑块 */
audio::-webkit-media-controls-timeline::after {
    content: '';
    display: block;
    width: 12px;
    height: 12px;
    background-color: #ff0000;
    border-radius: 50%;
    position: relative;
    top: -6px;
    left: 0;
}
/* Firefox兼容样式 */
@-moz-document url-prefix() {
    audio {
        background: rgba(0, 30, 85, 0.8);
        color: white;
    }
}
</style>