from typing import Iterator, BinaryIO

API_VERSION = "1.2_01"

import time

from .reader import FileReader


class ChunkerFixed:
    """
    This is a simple chunker for input data with data usually staying at the same
    offset and/or with known block/record sizes:

    - raw disk images
    - block devices
    - database files with simple header + fixed-size records layout

    It optionally supports:

    - a header block of different size
    - using a sparsemap to read only data ranges and seek over hole ranges
      for sparse files.
    - using an externally given filemap to read only specific ranges from
      a file.

    Note: the last block of a data or hole range may be less than the block size,
          this is supported and not considered to be an error.
    """

    def __init__(self, block_size: int, header_size: int = 0, sparse: bool = False) -> None:
        self.block_size = block_size
        self.header_size = header_size
        self.chunking_time = 0.0  # likely will stay close to zero - not much to do here.
        self.reader_block_size = 1024 * 1024
        self.reader: FileReader | None = None
        self.sparse = sparse

    def chunkify(self, fd: BinaryIO | None = None, fh: int = -1, fmap: list | None = None) -> Iterator:
        """
        Cut a file into chunks.

        :param fd: Python file object
        :param fh: OS-level file handle (if available),
                   defaults to -1 which means not to use OS-level fd.
        :param fmap: a file map, same format as generated by sparsemap
        """
        # Initialize the reader with the file descriptors
        self.reader = FileReader(fd=fd, fh=fh, read_size=self.reader_block_size, sparse=self.sparse, fmap=fmap)

        # Handle header if present
        if self.header_size > 0:
            # Read the header block using read
            started_chunking = time.monotonic()
            header_chunk = self.reader.read(self.header_size)
            self.chunking_time += time.monotonic() - started_chunking

            if header_chunk.meta["size"] > 0:
                # Yield the header chunk
                yield header_chunk

        # Process the rest of the file using read
        while True:
            started_chunking = time.monotonic()
            chunk = self.reader.read(self.block_size)
            self.chunking_time += time.monotonic() - started_chunking
            size = chunk.meta["size"]
            if size == 0:
                break  # EOF
            assert size <= self.block_size
            yield chunk
