""" Provide UI agnostics progress indication

There are many long running tasks throughout the core pymoku API.  This module
provides an interface for UI specific APIs to register progress handlers that
can intercept and display core library task progress without forcing
dependencies into the core library.

Any task wishing to display progress should use the progress() context manager

    with progress() as p:
        p.set_message("Doing stuff...")
        # do stuff
        p.set_progress(0.1)
        # do stuff
        p.set_progress(0.5)
        # do stuff
        p.set_message("Almost done doing stuff...")
        p.set_progress(0.9)

Ths instance p will be a _CompositeHandler which will proxy all registered
ProgressHandlers.

start() and complete() will be called with the context lifecycle.

Each invocation of progress() may result in new instances of ProgressHandler
to represent specific tasks (possible running concurrently).

# Note:  The ProgressHandler instance will be garbage collected after leaving
the context, including any objects it created.
"""
from contextlib import contextmanager


class ProgressHandler(object):
    def start(self):
        """ Start a new task

        This should return a new ProgressHandler instance for a specific task.

        This can be overridden as a classmethod to act as a factory method.
        """
        return self

    def set_progress(self, val: float):
        """ Update the progress value

        Args:
            val: float from 0.0 to 1.0
        """
        pass

    def set_message(self, message):
        """ Set a message describing the current activity
        """
        pass

    def complete(self):
        """ Mark the task as complete
        """
        pass


class _CompositeHandler(ProgressHandler):
    """ A group of handlers that can be treated as a single handler

    Many handlers can be registered, but a task should only care about updating
    a single instance.
    """
    def start(self):
        self._handlers = [h.start() for h in _handlers]
        return self

    def set_progress(self, val: float):
        [h.set_progress(val) for h in self._handlers]

    def set_message(self, message):
        [h.set_message(message) for h in self._handlers]

    def complete(self):
        [h.complete() for h in self._handlers]


_handlers = []


def register_progress_handler(handler):
    """ Register a ProgressHandler

    Args:
        handler: Either an instance or class object
    """
    _handlers.append(handler)


@contextmanager
def progress():
    handler = _CompositeHandler().start()
    try:
        yield handler
    finally:
        handler.complete()
