# ------------------------------------------------------------
# AAC Coder/Decoder - Level 2 Wrappers + Demo
#
# Multimedia course at Aristotle University of
# Thessaloniki (AUTh)
#
# Author:
#   Christos Choutouridis (ΑΕΜ 8997)
#   cchoutou@ece.auth.gr
#
# Description:
#   Level 2 wrapper module.
#
#   This file provides:
#   - Thin wrappers for Level 2 API functions (encode/decode) that delegate
#     to the corresponding core implementations.
#   - A demo function that runs end-to-end and computes SNR.
#   - A small CLI entrypoint for convenience.
# ------------------------------------------------------------
from __future__ import annotations

from pathlib import Path
from typing import Union

import soundfile as sf

from core.aac_types   import AACSeq2, StereoSignal
from core.aac_coder   import aac_coder_2 as core_aac_coder_2
from core.aac_coder   import aac_read_wav_stereo_48k
from core.aac_decoder import aac_decoder_2 as core_aac_decoder_2
from core.aac_utils  import snr_db

# -----------------------------------------------------------------------------
# Public Level 2 API (wrappers)
# -----------------------------------------------------------------------------

def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
    """
    Level-2 AAC encoder (wrapper).

    Delegates to core implementation.

    Parameters
    ----------
    filename_in : Union[str, Path]
        Input WAV filename.
        Assumption: stereo audio, sampling rate 48 kHz.

    Returns
    -------
    AACSeq2
        List of encoded frames (Level 2 schema).
    """
    return core_aac_coder_2(filename_in)


def i_aac_coder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoSignal:
    """
    Level-2 AAC decoder (wrapper).

    Delegates to core implementation.

    Parameters
    ----------
    aac_seq_2 : AACSeq2
        Encoded sequence as produced by aac_coder_2().
    filename_out : Union[str, Path]
        Output WAV filename. Assumption: 48 kHz, stereo.

    Returns
    -------
    StereoSignal
        Decoded audio samples (time-domain), stereo, shape (N, 2), dtype float64.
    """
    return core_aac_decoder_2(aac_seq_2, filename_out)


# -----------------------------------------------------------------------------
# Demo (Level 2)
# -----------------------------------------------------------------------------

def demo_aac_2(filename_in: Union[str, Path], filename_out: Union[str, Path]) -> float:
    """
    Demonstration for the Level-2 codec.

    Runs:
    - aac_coder_2(filename_in)
    - aac_decoder_2(aac_seq_2, filename_out)
    and computes total SNR between original and decoded audio.

    Parameters
    ----------
    filename_in : Union[str, Path]
        Input WAV filename (stereo, 48 kHz).
    filename_out : Union[str, Path]
        Output WAV filename (stereo, 48 kHz).

    Returns
    -------
    float
        Overall SNR in dB.
    """
    filename_in = Path(filename_in)
    filename_out = Path(filename_out)

    # Read original audio (reference) with the same validation as the codec.
    x_ref, fs_ref = aac_read_wav_stereo_48k(filename_in)
    if int(fs_ref) != 48000:
        raise ValueError("Input sampling rate must be 48 kHz.")

    # Encode / decode
    aac_seq_2 = aac_coder_2(filename_in)
    x_hat = i_aac_coder_2(aac_seq_2, filename_out)

    # Optional sanity: ensure output file exists and is readable
    _, fs_hat = sf.read(str(filename_out), always_2d=True)
    if int(fs_hat) != 48000:
        raise ValueError("Decoded output sampling rate must be 48 kHz.")

    return snr_db(x_ref, x_hat)


# -----------------------------------------------------------------------------
# CLI
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    # Example:
    #   cd level_2
    #   python -m level_2 input.wav output.wav
    #   or
    #   python -m level_2 material/LicorDeCalandraca.wav LicorDeCalandraca_out_l2.wav
    import sys

    if len(sys.argv) != 3:
        raise SystemExit("Usage: python -m level_2 <input.wav> <output.wav>")

    in_wav = Path(sys.argv[1])
    out_wav = Path(sys.argv[2])

    print(f"Encoding/Decoding {in_wav} to {out_wav}")
    snr = demo_aac_2(in_wav, out_wav)
    print(f"SNR = {snr:.3f} dB")
