# Tai Sakuma <tai.sakuma@cern.ch>

##__________________________________________________________________||
class MPEventLoopRunner(object):
    def __init__(self, communicationChannel):
        self.communicationChannel = communicationChannel
        self._original_readers = [ ]

    def begin(self): pass

    def run(self, eventLoop):
        self._original_readers.append(eventLoop.reader)
        self.communicationChannel.put(eventLoop)

    def end(self):
        # If eventLoops were executed in other processes, the readers
        # in the main process do not read the events; therefore, they
        # don't have the results. The readers in other processes read
        # the events. They have the results. The readers in other
        # process are pickled and sent back to the main process.
        # However, these returned readers are no longer the same
        # objects as the original readers in the main process.

        # The following lines of the code copy the results in the
        # returned readers to the original readers if they are
        # different objects.

        returned_readers = self.communicationChannel.receive()

        if len(self._original_readers) != len(returned_readers):
            import logging
            logging.warning("'" + self.__class__.__name__
                            + "': the same number of the readers were not received: "
                            + str(len(self._original_readers)) + " readers put, "
                            + str(len(returned_readers)) + " readers received"
                        )

        for original, returned in zip(self._original_readers, returned_readers):
            if original is returned: continue
            original.setResults(returned.results())

##__________________________________________________________________||

# Tai Sakuma <tai.sakuma@cern.ch>

##__________________________________________________________________||
class MPEventLoopRunner(object):

    """This class (concurrently) runs instances of ``EventLoop``.

    An instance of this class needs to be initialized with a
    ``CommunicationChannel``, a communication channel with workers
    that actually run the ``EventLoop``::

        runner = MPEventLoopRunner(communicationChannel)

    The method ``begin()`` does nothing in the current version::

        runner.begin()

    In older versions, multiple processes are forked in this method.


    Then, you can give an ``EventLoop`` with the method ``run()``::

        runner.run(eventLoop1)

    This class will send the event loop to a worker through the
    communication channel. The worker, then, runs the event loop.

    You can call the method ``run()`` mutiple times::

        runner.run(eventLoop2)
        runner.run(eventLoop3)
        runner.run(eventLoop4)

    If workers are in the background, this method immediately returns.
    Worker are running the event loops concurrently in the background.
    If the worker is in the foreground, this method won't return until
    the worker finishes running the event loop. In this case, a
    progress bar might be shown on the screen if the communication
    channel is configured so. Whether workers are in the background or
    foreground depends on the communication channel with which this
    class is initialized.

    After giving all event loops that you need to run to this class,
    you need to call the method ``end()``::

        runner.end()

    If workers are in the background, this method will wait until
    workers finish running all event loops. While waiting progress
    bars will be shown on the screen if the communication channel is
    configured so. If the worker is in the background, this method
    immediately returns.

    """

    def __init__(self, communicationChannel):
        self.communicationChannel = communicationChannel
        self._original_readers = [ ]

    def begin(self): pass

    def run(self, eventLoop):
        self._original_readers.append(eventLoop.reader)
        self.communicationChannel.put(eventLoop)

    def end(self):
        """If eventLoops were executed in other processes, the readers
        in the main process do not read the events; therefore, they
        don't have the results. The readers in other processes read
        the events. They have the results. The readers in other
        process are pickled and sent back to the main process.
        However, these returned readers are no longer the same
        objects as the original readers in the main process.

        The method copy the results in the returned readers to the
        original readers if they are different objects.

        """

        returned_readers = self.communicationChannel.receive()

        if len(self._original_readers) != len(returned_readers):
            import logging
            logging.warning("'" + self.__class__.__name__
                            + "': the same number of the readers were not received: "
                            + str(len(self._original_readers)) + " readers put, "
                            + str(len(returned_readers)) + " readers received"
                        )

        for original, returned in zip(self._original_readers, returned_readers):
            if original is returned: continue
            original.setResults(returned.results())

##__________________________________________________________________||

update comments in MPEventLoopRunner