import time
import copy
import warnings

from typing import List

from UniTAP.common.audio_mode import AudioFrameData
from UniTAP.dev.modules.capturer.capture import Capturer, CaptureConfig
from UniTAP.dev.modules.capturer.statuses import AudioCaptureStatus
from .result_audio import ResultAudioObject


class AudioCapturer:
    """
    Class `AudioCapturer` allows working with capturing audio frames on Sink (RX - receiver) side.
    You can `start` capturing in several modes, `stop` capturing, getting current `status` and result of capturing
    `capture_result`.
    """
    def __init__(self, capturer: Capturer):
        self.__capturer = capturer
        self.__result = ResultAudioObject()

    @property
    def status(self) -> AudioCaptureStatus:
        """
        Returns current audio capturer status.

        Returns:
            object of `AudioCaptureStatus` type
        """
        return self.__capturer.audio_capturer_status

    @property
    def capture_result(self) -> ResultAudioObject:
        """
        Returns result of audio capturing.

        Returns:
            object of `ResultAudioObject` type
        """
        return self.__result

    def start(self, frames_count=0, m_sec=0, timeout=None):
        """
        Start capturing. Possible some variants of capturing:
        - Capture with fixed frames count (will be captured fixed frames count and capturing will be stopped).
        - Capture with fixed audio duration (captures audio for the specified duration in milliseconds).
        - Capture without parameters - Live capturing (for getting frames you need to use functions `pop_element`)
        - Capture with timeout (maximum duration for capturing operations before stopping, in seconds).

        All results can be obtained using the function `capture_result`.

        Args:
            frames_count (int)
            m_sec (int)
        """
        config = CaptureConfig()
        config.audio = True
        config.type = CaptureConfig.Type.LIVE
        config.video = True

        self.__capturer.start_capture(config)

        self.__result.clear()
        self.__result.start_capture_time = time.time()

        if frames_count > 0:
            self.__result.buffer.extend(self.__capturer.capture_audio_by_n_frames(frames_count, timeout))
            self.__fill_audio_mode()
        elif m_sec > 0:
            self.__result.buffer.extend(self.__capturer.capture_audio_by_m_sec(m_sec))
            self.__fill_audio_mode()

    def __fill_audio_mode(self):
        if len(self.__result.buffer) > 0:
            self.__result.audio_mode.channel_count = self.__result.buffer[0].channel_count
            self.__result.audio_mode.bits = self.__result.buffer[0].sample_size
            self.__result.audio_mode.sample_rate = self.__result.buffer[0].sample_rate

    def stop(self):
        """
        Stop capture audio.
        """
        config = CaptureConfig()
        config.audio = True
        config.type = CaptureConfig.Type.LIVE
        config.video = True

        self.__capturer.stop_capture(config)
        self.__result.end_capture_time = time.time()

    def pop_element(self) -> List[AudioFrameData]:
        """
        Return first object of `AudioFrameData`.

        Returns:
            object of `AudioFrameData` type
        """
        if self.status == AudioCaptureStatus.Stop:
            warnings.warn("Audio capture is not working now. Please, turn it on.")
        captured_audio_frames = self.__capturer.capture_audio_by_n_frames(1)
        self.__result.buffer.extend(copy.deepcopy(captured_audio_frames))
        self.__fill_audio_mode()
        return captured_audio_frames[0]

    def pop_element_as_result_object(self) -> ResultAudioObject:
        """
        Return captured audio frame(objects of `AudioFrameData`) as `ResultAudioObject`.

        Returns:
            object of `ResultAudioObject` type
        """
        captured_audio_frames = self.__capturer.capture_audio_by_n_frames(1)

        res = ResultAudioObject()
        res.buffer.extend(captured_audio_frames)
        return res
