import logging
import math
from pathlib import Path
from typing import Iterator

from physiq_cloud.series_frame.fb_wrapper import FlatBufferWrapper
from physiqfb.SeriesFrame import SeriesFrame
from physiqfb.SeriesFrameHolderList import SeriesFrameHolderList

from pojagi_dsp.series_frame import (ASeriesFrameEmitter,
                                     SeriesFramePackage)


logger = logging.getLogger(__name__)


class FileSampler(ASeriesFrameEmitter):
    def __init__(
        self,
        *sfhl_files: Path,
        frame_count: int = math.inf,
        frame_offset: int = 0,
        **kwargs,
    ) -> None:
        """
        :param:`sfhl_files` - One or more files containing sfhl data. It's
        assumed that the files are sorted and contain contiguous (sorted)
        data, and no effort is made to validate that or handle any error
        condition related to correctness. The files are treated as a single
        logical file, and an `EOFError` is raised if the `frame_count` wants
        more than the available frames.

        :param:`frame_count` - The number of frames this generator will
        attempt to read from the given files.

        :param:`frame_offset` - The number of frames to skip over before
        yielding frames from the given files.
        """
        super().__init__(**kwargs)
        self.files = sfhl_files
        self.frame_offset = frame_offset
        self.frame_count = frame_count
        self.frame_idx = 0

    def frames_from_sfhl_bytes(
        self, data: bytes,
    ) -> Iterator[FlatBufferWrapper[SeriesFrame]]:
        """
        Emit frames from series frame holder list data.

        :param:`data` - sfhl bytes.
        """
        sfhl = SeriesFrameHolderList.GetRootAsSeriesFrameHolderList(data, 0)
        for index in range(sfhl.FramesLength()):
            yield FlatBufferWrapper(
                _bytes=sfhl.Frames(index).DataAsNumpy().tobytes(),
                schema=SeriesFrame,
            )

    def frames(self) -> Iterator[SeriesFramePackage]:
        self.frame_idx = 0
        end_frame_idx = self.frame_offset + self.frame_count

        for file in self.files:
            """
            Finite set of files passed in.
            """
            for frame in self.frames_from_sfhl_bytes(file.read_bytes()):
                """
                Finite set of frames from `read_bytes`.
                """
                if self.frame_idx < self.frame_offset:
                    """
                    Fast-forward to the declared offset, for each file.
                    """
                    self.frame_idx += 1
                    continue
                elif self.frame_idx > end_frame_idx:
                    """
                    `end_frame_idx` is finite.
                    """
                    break

                yield SeriesFramePackage(frame=frame, sft_info=self.sft_info)
                self.frame_idx += 1

        if end_frame_idx != math.inf:
            if self.frame_idx < end_frame_idx:
                frames_read = self.frame_idx - self.frame_offset
                raise EOFError(
                    f"Failed to read {self.frame_count - frames_read} frames. "
                    "Not enough data.\n"
                    f"\toffset      : {self.frame_offset}\n"
                    f"\tframes read : {frames_read}"
                )
