# @Coding: UTF-8
# @Time: 2024/9/17 16:48
# @Author: xieyang_ls
# @Filename: thread_executor.py

from typing import Any

from threading import Thread

from pyutils_spirit.util.set import Set, HashSet

from pyutils_spirit.concurrent_thread.lock import ReentryLock

from pyutils_spirit.concurrent_thread.que_ue import Queue, BlockingQueue


class ThreadExecutor:

    def __init__(self, executor_count: int = 5, timeout: int = 2, task_queue_capacity: int = 10):
        self.__lock: ReentryLock = ReentryLock()
        self.__executor_count: int = executor_count
        self.__timeout: int = timeout
        self.__task_queue_capacity: int = task_queue_capacity
        self.__callable_dict_queue: Queue[dict] = BlockingQueue(max_capacity=self.__task_queue_capacity,
                                                                block_time=self.__timeout)
        self.__executors: Set[Thread] = HashSet()

    def execute(self, task: callable, *args: Any, **kwargs: Any) -> None:
        self.__lock.try_lock()
        callable_dict: dict = {
            "callable": task,
            "args": args,
            "kwargs": kwargs
        }
        try:
            if len(self.__executors) < self.__executor_count:
                executor = ThreadExecutor.__Executor(lock=self.__lock,
                                                     callable_dict=callable_dict,
                                                     executors=self.__executors,
                                                     callable_dict_queue=self.__callable_dict_queue)
                self.__executors.add(executor)
                executor.start()
                return None
        finally:
            self.__lock.release()
        self.__callable_dict_queue.addTail(callable_dict)

    class __Executor(Thread):

        def __init__(self,
                     lock: ReentryLock,
                     callable_dict: dict,
                     executors: Set[Thread],
                     callable_dict_queue: Queue[callable]) -> None:
            super().__init__()
            self.__lock: ReentryLock = lock
            self.__executors: Set[Thread] = executors
            self.__callable_dict_queue: Queue[dict] = callable_dict_queue
            self.__args: tuple = callable_dict["args"]
            self.__kwargs: dict = callable_dict["kwargs"]
            self.__task: callable = callable_dict["callable"]

        def run(self) -> None:
            while True:
                self.__task(*self.__args, **self.__kwargs)
                callable_dict: dict = self.__callable_dict_queue.removeHead()
                if callable_dict is None:
                    break
                self.__args: tuple = callable_dict["args"]
                self.__kwargs: dict = callable_dict["kwargs"]
                self.__task: callable = callable_dict["callable"]
            self.__lock.try_lock()
            try:
                self.__executors.remove(self)
            finally:
                self.__lock.release()
