from procgraph import Generator, Block, BadConfig
from procgraph.block_utils import expand
import os
import re

__all__ = ["FilesFromDir"]


class FilesFromDir(Generator):
    """
        This block reads the filenames from a directory according
        to a given regexp.

        For now the timestamp starts from 0 and it is fixed.
    """

    Block.alias("files_from_dir")
    Block.config("dir", "Directory containing the image files.")
    Block.config(
        "regexp",
        "Regular expression for images.",
        #                  default='(\w+)(\d+)\.(\w+)')
        default=".+\.(png|jpg)",
    )
    Block.config("fps", "Fixed frame per second.", default=20.0)
    Block.output("filename", "Image filename")

    def init(self):
        dirname = self.config.dir
        dirname = expand(dirname)

        if not os.path.exists(dirname):
            raise BadConfig("Not existent directory %r." % dirname, self, "dir")

        if not os.path.isdir(dirname):
            raise BadConfig("The file %r is not a directory." % dirname, self, "dir")

        regexp = self.config.regexp

        # TODO: use proper logging
        self.info("Reading %r from %r." % (regexp, dirname))
        all_files = os.listdir(dirname)

        selected = [os.path.join(dirname, f) for f in all_files if re.match(regexp, f)]

        def natural_key(string):
            """See http://www.codinghorror.com/blog/archives/001018.html"""
            return [int(s) if s.isdigit() else s for s in re.split(r"(\d+)", string)]

        selected.sort(key=lambda x: natural_key(os.path.basename(x)))

        self.info(f"Selected {len(selected)}/{len(all_files)} files.")

        fps = float(self.config.fps)
        if fps <= 0:
            raise BadConfig(self, f"Invalid fps value {fps!r}.", "fps")
        # tuples (timestamp, filename)
        frames = [(i / fps, f) for i, f in enumerate(selected)]

        if not frames:
            raise Exception("No frames found in dir %r." % dirname)

        self.state.frames = frames
        self.state.next_frame = 0

    def next_data_status(self):
        k = self.state.next_frame
        if k is None:
            return False, None
        else:
            frames = self.state.frames
            timestamp = frames[k][0]
            return True, timestamp

    def update(self):
        frames = self.state.frames
        k = self.state.next_frame

        assert k < len(frames)

        timestamp, filename = frames[k]

        self.set_output(0, value=filename, timestamp=timestamp)

        if k + 1 >= len(frames):
            self.state.next_frame = None
        else:
            self.state.next_frame = k + 1
