cimport libav as lib
from cpython.bytes cimport PyBytes_FromStringAndSize

from dataclasses import dataclass


@dataclass
class AudioChannel:
    name: str
    description: str

    def __repr__(self):
        return f"<av.AudioChannel '{self.name}' ({self.description})>"

cdef object _cinit_bypass_sentinel

cdef AudioLayout get_audio_layout(lib.AVChannelLayout c_layout):
    """Get an AudioLayout from Cython land."""
    cdef AudioLayout layout = AudioLayout.__new__(AudioLayout, _cinit_bypass_sentinel)
    layout._init(c_layout)
    return layout


cdef class AudioLayout:
    def __init__(self, layout):
        if layout is _cinit_bypass_sentinel:
            return

        if type(layout) is str:
            ret = lib.av_channel_layout_from_string(&c_layout, layout)
            if ret != 0:
                raise ValueError(f"Invalid layout: {layout}")
        elif isinstance(layout, AudioLayout):
            c_layout = (<AudioLayout>layout).layout
        else:
            raise TypeError(f"layout must be of type: string | av.AudioLayout, got {type(layout)}")

        self._init(c_layout)

    cdef _init(self, lib.AVChannelLayout layout):
        self.layout = layout

    def __repr__(self):
        return f"<av.{self.__class__.__name__} {self.name!r}>"

    def __eq__(self, other):
        return isinstance(other, AudioLayout) and self.name == other.name and self.nb_channels == other.nb_channels

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

    @property
    def channels(self):
        cdef char buf[16]
        cdef char buf2[128]

        results = []

        for index in range(self.layout.nb_channels):
            size = lib.av_channel_name(buf, sizeof(buf), lib.av_channel_layout_channel_from_index(&self.layout, index)) - 1
            size2 = lib.av_channel_description(buf2, sizeof(buf2), lib.av_channel_layout_channel_from_index(&self.layout, index)) - 1
            results.append(
                AudioChannel(
                    PyBytes_FromStringAndSize(buf, size).decode("utf-8"),
                    PyBytes_FromStringAndSize(buf2, size2).decode("utf-8"),
                )
            )

        return tuple(results)

    @property
    def name(self) -> str:
        """The canonical name of the audio layout."""
        cdef char layout_name[128]
        cdef int ret

        ret = lib.av_channel_layout_describe(&self.layout, layout_name, sizeof(layout_name))
        if ret < 0:
            raise RuntimeError(f"Failed to get layout name: {ret}")

        return layout_name