from functools import cached_property

from OTVision.abstraction.observer import Subject
from OTVision.application.event.new_video_start import NewVideoStartEvent
from OTVision.detect.builder import DetectBuilder
from OTVision.detect.detected_frame_buffer import FlushEvent
from OTVision.detect.video_input_source import VideoSource
from OTVision.domain.video_writer import VideoWriter
from OTVision.plugin.ffmpeg_video_writer import (
    FfmpegVideoWriter,
    PixelFormat,
    VideoFormat,
    append_save_suffix_to_save_location,
)


class FileBasedDetectBuilder(DetectBuilder):

    @cached_property
    def input_source(self) -> VideoSource:
        return VideoSource(
            subject_flush=Subject[FlushEvent](),
            subject_new_video_start=Subject[NewVideoStartEvent](),
            get_current_config=self.get_current_config,
            frame_rotator=self.frame_rotator,
            timestamper_factory=self.timestamper_factory,
            save_path_provider=self.detection_file_save_path_provider,
        )

    @cached_property
    def video_file_writer(self) -> VideoWriter:
        # Using save_location_strategy=keep_original_save_location is not supported for
        # file-based detection. Otherwise, we would be overwriting the input source that
        # we are reading from.
        return FfmpegVideoWriter(
            save_location_strategy=append_save_suffix_to_save_location,
            encoding_speed=self.detect_config.encoding_speed,
            input_format=VideoFormat.RAW,
            output_format=VideoFormat.MP4,
            input_pixel_format=PixelFormat.RGB24,
            output_pixel_format=PixelFormat.YUV420P,
            output_video_codec=self.detect_config.video_codec,
            constant_rate_factor=self.detect_config.crf,
        )

    def register_observers(self) -> None:
        if self.detect_config.write_video:
            self.input_source.subject_new_video_start.register(
                self.video_file_writer.notify_on_new_video_start
            )
            self.input_source.subject_flush.register(
                self.video_file_writer.notify_on_flush_event
            )
        self.input_source.subject_flush.register(self.detected_frame_buffer.on_flush)
        self.detected_frame_buffer.register(self.otdet_file_writer.write)
