# Tai Sakuma <tai.sakuma@gmail.com>
import logging
import time
from operator import itemgetter

from .WorkingArea import WorkingArea

##__________________________________________________________________||
class TaskPackageDropbox(object):
    """A drop box for task packages.

    It puts task packages in a working area and dispatches runners
    that execute the tasks.

    """
    def __init__(self, workingArea, dispatcher, sleep=5):
        self.workingArea = workingArea
        self.dispatcher = dispatcher
        self.sleep = sleep

    def __repr__(self):
        name_value_pairs = (
            ('workingArea', self.workingArea),
            ('dispatcher', self.dispatcher),
        )
        return '{}({})'.format(
            self.__class__.__name__,
            ', '.join(['{}={!r}'.format(n, v) for n, v in name_value_pairs]),
        )

    def open(self):
        self.workingArea.open()
        self.runid_package_index_map = { }

    def put(self, package):

        package_index = self.workingArea.put_package(package)

        logger = logging.getLogger(__name__)
        logger.info('submitting {}'.format(self.workingArea.package_path(package_index)))

        runid = self.dispatcher.run(self.workingArea, package_index)
        self.runid_package_index_map[runid] = package_index

    def receive(self):
        pkgidx_result_pairs = [ ] # a list of (package_index, _result)
        while self.runid_package_index_map:

            pairs = self._collect_pkgidx_result_pairs_of_finished_tasks()
            pkgidx_result_pairs.extend(pairs)

            time.sleep(self.sleep)

        # sort in the order of package_index
        pkgidx_result_pairs = sorted(pkgidx_result_pairs, key=itemgetter(0))

        results = [result for i, result in pkgidx_result_pairs]
        return results

    def _collect_pkgidx_result_pairs_of_finished_tasks(self):

        finished_runid = self.dispatcher.poll()
        # e.g., [1001, 1003]

        runid_pkgidx = [(i, self.runid_package_index_map.pop(i)) for i in finished_runid]
        # e.g., [(1001, 0), (1003, 2)]

        runid_pkgidx_result = [(ri, pi, self.workingArea.collect_result(pi)) for ri, pi in runid_pkgidx]
        # e.g., [(1001, 0, result0), (1003, 2, None)] # None indicates the job failed

        failed = [e for e in runid_pkgidx_result if e[2] is None]
        # e.g., [(1003, 2, None)]

        succeeded = [e for e in runid_pkgidx_result if e not in failed]
        # e.g., [(1001, 0, result0)]

        # let the dispatcher know the failed runid
        failed_runid = [e[0] for e in failed]
        self.dispatcher.failed_runids(failed_runid)

        # rerun failed jobs
        for _, pkgidx, _ in failed:
            logger = logging.getLogger(__name__)
            logger.warning('resubmitting {}'.format(self.workingArea.package_path(pkgidx)))

            runid = self.dispatcher.run(self.workingArea, pkgidx)
            self.runid_package_index_map[runid] = pkgidx

        pairs = [(pkgidx, result) for runid, pkgidx, result in succeeded]
        # e.g., [(0, result0)] # only successful ones

        return pairs

    def terminate(self):
        self.dispatcher.terminate()

    def close(self):
        self.workingArea.close()

##__________________________________________________________________||

@@ -917,35 +917,28 @@
 self.runid_p
-ackage_inde
+kgid
 x_map = %7B %7D%0A
@@ -976,27 +976,20 @@
        p
-ackage_inde
+kgid
 x = self
@@ -1142,27 +1142,20 @@
 e_path(p
-ackage_inde
+kgid
 x)))%0A%0A  
@@ -1207,27 +1207,20 @@
 gArea, p
-ackage_inde
+kgid
 x)%0A     
@@ -1230,35 +1230,28 @@
 self.runid_p
-ackage_inde
+kgid
 x_map%5Brunid%5D
@@ -1254,27 +1254,20 @@
 nid%5D = p
-ackage_inde
+kgid
 x%0A%0A    d
@@ -1332,27 +1332,20 @@
 st of (p
-ackage_inde
+kgid
 x, _resu
@@ -1370,35 +1370,28 @@
 self.runid_p
-ackage_inde
+kgid
 x_map:%0A%0A    
@@ -1575,27 +1575,20 @@
 der of p
-ackage_inde
+kgid
 x%0A      
@@ -1924,35 +1924,28 @@
 self.runid_p
-ackage_inde
+kgid
 x_map.pop(i)
@@ -2864,19 +2864,12 @@
 id_p
-ackage_inde
+kgid
 x_ma

rename variables in TaskPackageDropbox