from typing import Type

import sounddevice as sd

from platypush.message.event.sound import (
    SoundRecordingPausedEvent,
    SoundRecordingResumedEvent,
    SoundRecordingStartedEvent,
    SoundRecordingStoppedEvent,
)

from .._converters import RawInputAudioConverter
from .._model import AudioState, StreamType
from ._base import AudioThread


class AudioRecorder(AudioThread):
    """
    The ``AudioRecorder`` thread is responsible for recording audio from the
    input device, writing it to the converter process and dispatch the
    converted audio to the registered consumers.
    """

    def __init__(self, *args, output_format: str, **kwargs):
        super().__init__(*args, **kwargs)
        self.output_format = output_format

    @property
    def direction(self) -> StreamType:
        return StreamType.INPUT

    def _audio_callback(self):
        # _ = frames
        # __ = time
        def callback(indata, outdata, _, __, status):
            if self.state != AudioState.RUNNING:
                return

            if status:
                self.logger.warning('Recording callback status: %s', status)

            if not self._converter:
                self.logger.warning(
                    'The ffmpeg converter process has already terminated'
                )
                self.notify_stop()
                raise sd.CallbackStop

            try:
                self._converter.write(indata.tobytes())
            except AssertionError as e:
                self.logger.warning('Audio converter callback error: %s', e)
                self.state = AudioState.STOPPED
                return

            if self.audio_pass_through:
                outdata[:] = indata

        return callback

    @property
    def _audio_converter_type(self) -> Type[RawInputAudioConverter]:
        return RawInputAudioConverter

    @property
    def _started_event_type(self) -> Type[SoundRecordingStartedEvent]:
        return SoundRecordingStartedEvent

    @property
    def _stopped_event_type(self) -> Type[SoundRecordingStoppedEvent]:
        return SoundRecordingStoppedEvent

    @property
    def _paused_event_type(self) -> Type[SoundRecordingPausedEvent]:
        return SoundRecordingPausedEvent

    @property
    def _resumed_event_type(self) -> Type[SoundRecordingResumedEvent]:
        return SoundRecordingResumedEvent

    @property
    def _converter_args(self) -> dict:
        return {
            'output_format': self.output_format,
            **super()._converter_args,
        }

    @property
    def _stream_args(self) -> dict:
        return {
            'callback': self._audio_callback(),
            **super()._stream_args,
        }


# vim:sw=4:ts=4:et:
