from enum import IntEnum
from UniTAP.common.timestamp import Timestamp


class AudioFileFormat(IntEnum):
    """
    Describe all supported audio file formats for saving audio:
    - BIN.
    - WAV.
    """
    UNKNOWN = -1
    BIN = 0
    WAV = 1


class AudioFormat(IntEnum):
    """
    Describe all supported audio formats:
    - PCMAudio.
    """
    Unknown = -1
    L_PCM = 0xFFFF


class AudioMode:
    """
    Class `AudioMode` contains part information of audio: sample rate, count of bits and channel count.
    """
    def __init__(self, sample_rate: int = 44100, bits: int = 16, channel_count: int = 2):
        self.sample_rate = sample_rate
        self.bits = bits
        self.channel_count = channel_count

    def __str__(self):
        return f"Sample rate: {self.sample_rate}\n" \
               f"Bits: {self.bits}\n" \
               f"Channel count: {self.channel_count}\n"

    def __eq__(self, other):
        return self.sample_rate == other.sample_rate and \
               self.bits == other.bits and \
               self.channel_count == other.channel_count

    def is_valid(self) -> bool:
        """

        Check that information is valid (all values more than 0).

        Returns:
            object of bool type.
        """
        return self.sample_rate > 0 and \
               self.bits > 0 and \
               self.channel_count > 0


class AudioFrameData:
    """
    Class `AudioFrameData` describes captured frame from Sink (RX - receiver) side. Contains information of audio:
    `AudioMode`, samples, `AudioFormat`, frame counter, `Timestamp`, audio data.
    """
    def __init__(self, audio_mode: AudioMode = AudioMode(), samples: int = 0,
                 sample_format: AudioFormat = AudioFormat.Unknown, frame_counter: int = 0,
                 timestamp: Timestamp = Timestamp(0), data: bytearray = bytearray()):
        self.__audio_mode = audio_mode
        self.__samples = samples
        self.__sample_format = sample_format
        self.__frame_counter = frame_counter
        self.__timestamp = timestamp
        self.__data = data

    @property
    def channel_count(self) -> int:
        """

        Returns channel count.

        Returns:
            object of int type.
        """
        return self.__audio_mode.channel_count

    @property
    def samples(self) -> int:
        """

        Returns samples.

        Returns:
            object of int type.
        """
        return self.__samples

    @property
    def sample_size(self) -> int:
        """

        Returns sample size.

        Returns:
            object of int type.
        """
        return self.__audio_mode.bits

    @property
    def sample_rate(self) -> int:
        """

        Returns sample rate.

        Returns:
            object of int type.
        """
        return self.__audio_mode.sample_rate

    @property
    def sample_format(self) -> AudioFormat:
        """

        Returns sample format.

        Returns:
            object of AudioFormat type.
        """
        return self.__sample_format

    @property
    def frame_counter(self) -> int:
        """

        Returns frame counter.

        Returns:
            object of int type.
        """
        return self.__frame_counter

    @property
    def timestamp(self) -> Timestamp:
        """

        Returns timestamp.

        Returns:
            object of Timestamp type.
        """
        return self.__timestamp

    @property
    def data(self) -> bytearray:
        """

        Returns data.

        Returns:
            object of bytearray type.
        """
        return self.__data

    @channel_count.setter
    def channel_count(self, channel_count: int):
        """

        Allows setting new value to channel count.

        Args:
            channel_count (int) - must be more than 0
        """
        if channel_count <= 0:
            raise ValueError(f"Channel count must be more than 0.")
        self.__audio_mode.channel_count = channel_count

    @samples.setter
    def samples(self, samples: int):
        """

        Allows setting new value to samples.

        Args:
            samples (int) - must be more than 0
        """
        if samples <= 0:
            raise ValueError(f"Samples must be more than 0.")
        self.__samples = samples

    @sample_size.setter
    def sample_size(self, sample_size: int):
        """

        Allows setting new value to sample size.

        Args:
            sample_size (int) - must be more than 0
        """
        if sample_size <= 0:
            raise ValueError(f"Sample size must be more than 0.")
        self.__audio_mode.bits = sample_size

    @sample_rate.setter
    def sample_rate(self, sample_rate: int):
        """

        Allows setting new value to sample rate.

        Args:
            sample_rate (int) - must be more than 0
        """
        if sample_rate <= 0:
            raise ValueError(f"Sample rate must be more than 0.")
        self.__audio_mode.sample_rate = sample_rate

    @sample_format.setter
    def sample_format(self, sample_format: int):
        """

        Allows setting new value to sample format.

        Args:
            sample_format (int) - must be more than 0
        """
        if sample_format <= 0:
            raise ValueError(f"Sample format must be more than 0.")
        self.__sample_format = AudioFormat(sample_format)

    @timestamp.setter
    def timestamp(self, timestamp: int):
        """

        Allows setting new value to timestamp.

        Args:
            timestamp (int) - must be more than 0
        """
        if timestamp <= 0:
            raise ValueError(f"Timestamp must be more than 0.")
        self.__timestamp.value = timestamp

    @frame_counter.setter
    def frame_counter(self, frame_counter: int):
        """

        Allows setting new value to frame counter.

        Args:
            frame_counter (int) - must be more than -1
        """
        if frame_counter < 0:
            raise ValueError(f"Frame counter must be more than 0.")
        self.__frame_counter = frame_counter

    @data.setter
    def data(self, value: bytearray):
        """

        Allows setting new value to data.

        Args:
            value (bytearray) - length of value must be more than 0
        """
        if len(value) <= 0:
            raise ValueError(f"Audio data length must be more than 0.")
        self.__data = value

    def __str__(self):
        return f"Sample rate: {self.sample_rate}\n" \
               f"Bits (Sample size): {self.sample_size}\n" \
               f"Channel count: {self.channel_count}\n" \
               f"Sample format: {self.sample_format.name}\n" \
               f"Timestamp: {self.timestamp.to_n_sec} n sec\n" \
               f"Frame counter: {self.frame_counter}\n" \
               f"Length of data: {self.data} bytes\n"
