//
//  AMRCodec.swift
//  ylgamehall
//
//  AMR-NB ↔ WAV 文件级编解码的 Swift 薄 wrapper。
//
//  ## 为何如此
//
//  AMR-NB 是 H5 语音功能的跨端契约硬约束（iOS / Android / 旧版本客户端都按
//  AMR-NB 解码 audiourl），格式不能换。详见 Vendor/OpenCoreAMR/README.md。
//
//  实际编解码由 daoqi 沿用的 `amrFileCodec.mm`（C++ 文件级操作 — 解析 WAV
//  header、按 PCM 帧调 opencore-amr，写 AMR 文件头/帧）执行，本文件只是 Swift
//  侧的薄包装。
//
//  - 不在 Swift 层重写 amrFileCodec：那几百行 C 风格 byte 流操作（FILE* /
//    memcpy / 字节序解析）是 Swift 最别扭的领域，重写收益小风险大；daoqi 版
//    本已上线 N 年稳定运行
//  - amrFileCodec.{h,mm} 放在 Source/Audio/AMRFileCodec/，由 synchronized
//    group 自动编译；amrFileCodec.h 通过 ylgamehall-Bridging-Header.h 暴露
//    给 Swift
//
//  ## 用途
//
//  - 录音链路（prepareaudio）：`wavToAmr(_:to:)` 把 AVAudioRecorder 录的 wav
//    转 amr 后上传七牛
//  - 播放链路（mediaTypeAudio）：`amrToWav(_:to:)` 把七牛下载的 amr 转 wav 后
//    交 AVAudioPlayer 播
//
//  契约：docs/H5-Native-Contract.md §3.1 ［4］［5］ + §3.2 ［9］
//

import Foundation

public enum AMRCodec {

    public enum Error: Swift.Error, Sendable {
        /// EncodeWAVEFileToAMRFile 返回 0（fopen 失败或写 0 帧）
        case wavToAmrFailed(wav: String, amr: String)
        /// DecodeAMRFileToWAVEFile 返回 0（amr 文件不存在 / 头校验失败 / wav 创建失败）
        case amrToWavFailed(amr: String, wav: String)
    }

    /// WAV → AMR。
    ///
    /// 录音的 wav 必须满足 8kHz / 16bit / mono（VoiceRecorderBaseVC.m 的
    /// `getAudioRecorderSettingDict` 输出，msext 多年约定）；否则
    /// amrFileCodec 的 ReadPCMFrame 拿不到完整 160 sample 帧，返回 0。
    ///
    /// - Throws: `AMRCodec.Error.wavToAmrFailed` 当底层 C 函数返回 0
    public static func wavToAmr(_ wav: URL, to amr: URL) throws {
        let frames = EncodeWAVEFileToAMRFile(wav.path, amr.path, 1, 16)
        guard frames > 0 else {
            throw Error.wavToAmrFailed(wav: wav.path, amr: amr.path)
        }
    }

    /// AMR → WAV。
    ///
    /// 解码后的 wav 是 8kHz / 16bit / mono，可直接交 AVAudioPlayer 播放。
    ///
    /// - Throws: `AMRCodec.Error.amrToWavFailed` 当底层 C 函数返回 0
    public static func amrToWav(_ amr: URL, to wav: URL) throws {
        let frames = DecodeAMRFileToWAVEFile(amr.path, wav.path)
        guard frames > 0 else {
            throw Error.amrToWavFailed(amr: amr.path, wav: wav.path)
        }
    }
}
