import logging
from typing import Callable, Iterator

from physiq_cloud.time import Duration, Instant, TimeUnit

from pojagi_dsp.series_frame import SeriesFrameFilter, SeriesFramePackage

logger = logging.getLogger(__name__)


class Updating(SeriesFrameFilter):
    """
    Filter that outputs frames until it runs out or the next frame would
    surpass a given :class:`Instant` in time, whichever comes first.
    """

    def __init__(
        self,
        until: Callable[[], Instant] = Instant.now,
        **kwargs,
    ):
        """
        :param:`until` - closure returning an `Instant` at which to stop
        iterating when the next frame would surpass it. The cursor will
        otherwise persist until it has been exhausted.
        """
        super().__init__(**kwargs)
        self.until = until
        self.last_update_count = 0
        self._next_pkg = None

    def log_status(filter, end: Instant, until: Instant):
        between = Duration.between(end, until).to_unix(TimeUnit.MILLISECONDS) / float(
            1000
        )

        logger.debug(
            f"{filter.sft_info.alias}: Update scope : {between} seconds"
            if end < until
            else (
                f"{filter.sft_info.alias}: "
                f"Next update available in {-between} seconds"
            )
        )

    def frames(self) -> Iterator[SeriesFramePackage]:
        self.last_update_count = 0

        if self._next_pkg is None:
            cursor = self.reader.stream()
            self._next_pkg = next(cursor)
        else:
            cursor = self.reader.cursor

        logger.debug(f"{self.sft_info.alias}: {cursor}")

        try:
            until = self.until()
            end = self._next_pkg.end

            self.log_status(end, until)

            while end < until:
                yield self._next_pkg
                self.last_update_count += 1
                self._next_pkg = next(cursor)
                end = self._next_pkg.end
        except StopIteration:
            logger.warning("StopIteration caught")
            logger.warning(f"{self.sft_info.alias}: {cursor}")
            ...

        if self.last_update_count:
            logger.debug(
                f"{self.sft_info.alias}: " f"Update frames : {self.last_update_count}"
            )
