import cython
from cython.cimports import libav as lib
from cython.cimports.av.audio.format import AudioFormat, get_audio_format
from cython.cimports.av.audio.frame import AudioFrame, alloc_audio_frame
from cython.cimports.av.audio.layout import AudioLayout, get_audio_layout
from cython.cimports.av.frame import Frame
from cython.cimports.av.packet import Packet


@cython.final
@cython.cclass
class AudioCodecContext(CodecContext):
    @cython.cfunc
    def _prepare_frames_for_encode(self, input_frame: Frame | None) -> list:
        frame: AudioFrame | None = input_frame
        allow_var_frame_size: cython.bint = (
            self.ptr.codec.capabilities & lib.AV_CODEC_CAP_VARIABLE_FRAME_SIZE
        )

        # Note that the resampler will simply return an input frame if there is
        # no resampling to be done. The control flow was just a little easier this way.
        if not self.resampler:
            self.resampler = AudioResampler(
                format=self.format,
                layout=self.layout,
                rate=self.ptr.sample_rate,
                frame_size=None if allow_var_frame_size else self.ptr.frame_size,
            )
        frames = self.resampler.resample(frame)
        if input_frame is None:
            frames.append(None)  # flush if input frame is None

        return frames

    @cython.cfunc
    def _alloc_next_frame(self) -> Frame:
        return alloc_audio_frame()

    @cython.cfunc
    def _setup_decoded_frame(self, frame: Frame, packet: Packet):
        CodecContext._setup_decoded_frame(self, frame, packet)
        aframe: AudioFrame = frame
        aframe._init_user_attributes()

    @property
    def frame_size(self):
        """
        Number of samples per channel in an audio frame.

        :type: int
        """
        return self.ptr.frame_size

    @property
    def sample_rate(self):
        """
        Sample rate of the audio data, in samples per second.

        :type: int
        """
        return self.ptr.sample_rate

    @sample_rate.setter
    def sample_rate(self, value: cython.int):
        self._assert_not_open("sample_rate")
        self.ptr.sample_rate = value

    @property
    def rate(self):
        """Another name for :attr:`sample_rate`."""
        return self.sample_rate

    @rate.setter
    def rate(self, value):
        self.sample_rate = value

    @property
    def channels(self):
        return self.layout.nb_channels

    @property
    def layout(self):
        """
        The audio channel layout.

        :type: AudioLayout
        """
        return get_audio_layout(self.ptr.ch_layout)

    @layout.setter
    def layout(self, value):
        self._assert_not_open("layout")
        layout: AudioLayout = AudioLayout(value)
        self.ptr.ch_layout = layout.layout

    @property
    def format(self):
        """
        The audio sample format.

        :type: AudioFormat
        """
        return get_audio_format(self.ptr.sample_fmt)

    @format.setter
    def format(self, value):
        self._assert_not_open("format")
        format: AudioFormat = AudioFormat(value)
        self.ptr.sample_fmt = format.sample_fmt
