# -*- coding: utf-8 -*-
import queue
import threading
import contextlib

StopEvent = object()


class DExecutor(object):

    def __init__(self, maxsize=1):
        self.q = queue.Queue()
        self.maxsize = maxsize
        self.terminal = False
        self.generate_list = []
        self.free_list = []
        self.lock = threading.Lock()

    def generate_thread(self):
        t = threading.Thread(target=self.call)
        t.start()

    @contextlib.contextmanager
    def worker_state(self, lst, t):
        lst.append(t)
        try:
            yield
        finally:
            lst.remove(t)

    def close(self):
        with self.lock:
            num = len(self.generate_list)
            while num:
                self.q.put(StopEvent)
                num -= 1

    def terminate(self):
        with self.lock:
            self.terminal = True
            while self.generate_list:
                self.q.put(StopEvent)

    def submit(self, func, *args, callback=None):
        """
        提交一个任务

        func: 任务函数。

        args: 任务函数所需的参数。

        callback: 任务执行完毕后调用的回调函数，回调函数有两个参数:
                         1、任务函数执行状态（True表示成功，False表示失败）；
                         2、任务函数返回值或异常。

        return: 如果线程池正在终止，则返回True，否则返回None。
        """
        if self.terminal:
            return True

        if len(self.free_list) == 0 and len(self.generate_list) < self.maxsize:
            self.generate_thread()

        task = (func, args, callback)
        self.q.put(task)

    def run(self, func, *args):
        """
        执行一个任务，并等待结果
        func: 任务函数。

        args: 任务函数所需的参数。

        return: 状态, 结果值
        """
        if self.terminal:
            return False, None

        result_queue = queue.Queue()

        def wrapper(status, result):
            result_queue.put((status, result))

        self.run(func, *args, callback=wrapper)

        status, result = result_queue.get()
        return status, result

    def call(self):
        current_thread = threading.currentThread()
        with self.lock:
            self.generate_list.append(current_thread)

        while True:
            event = self.q.get()
            if event == StopEvent:
                break

            func, arguments, callback = event
            try:
                result = func(*arguments)
                status = True
            except Exception as e:
                status = False
                result = e

            if callback:
                try:
                    callback(status, result)
                except Exception:
                    pass

            if self.terminal:
                break

            with self.worker_state(self.free_list, current_thread):
                if self.terminal:
                    break

        with self.lock:
            self.generate_list.remove(current_thread)
