import functools
import json
import threading
from concurrent.futures import ThreadPoolExecutor

from unified_service.caller import HubCaller
import logging
import grpc

HUB_ADDRESS = '127.0.0.1:5307'
VIS_PROJECT = ""


class adapter:
    def __init__(self, *args):
        super().__init__(*args)
        self.is_stop_adapter = False
        self.vision_project_name = VIS_PROJECT
        self.hub_caller = HubCaller(HUB_ADDRESS)
        self.async_vision_future = {}
        self.thread_pool = ThreadPoolExecutor(max_workers=4, thread_name_prefix="vision_thread")
        self.async_vision_lock = threading.Lock()

    def call(self, service_name, message, timeout=None):
        if message.get("function") != 'getDigitalIn':
            logging.info("Call service:{}, message:{}".format(service_name, message))
        if not hasattr(HubCaller, "is_supports_timeout"):  # Just for forward compatible
            return self.hub_caller.call("forward", {"name": service_name, "message": message})
        else:
            return self.hub_caller.call("forward", {"name": service_name, "message": message}, timeout=timeout)

    def call_vision(self, func_name, msg={}, project_name=None, timeout=None):
        msg["function"] = func_name
        #if "findPoses" == func_name:
        #    msg[jk.use_robot_jps] = msg.get(jk.use_robot_jps, 1)
        service_name = project_name if project_name else self.vision_project_name
        try:
            return self.call(service_name, msg, timeout)
        except grpc.FutureTimeoutError:
            return "{}".encode()

    def async_call_vision_run(self, project_name, timeout=10):
        logging.info("Asynchronously call vision: {}".format(project_name))
        future = self.thread_pool.submit(self.call_vision, "findPoses", project_name=project_name, timeout=timeout)
        with self.async_vision_lock:
            self.async_vision_future[project_name] = future
        # It means: self.async_get_vision_callback(future, project_name)
        future.add_done_callback(functools.partial(self.async_get_vision_callback, project_name))
        return True

    def async_get_vision_callback(self, project_name, future=None):
        """Copy this function"""
        logging.info("Asynchronously get vision pose by callback: {}".format(project_name))
        return self.get_async_data(project_name,
                                   json.loads(
                                       self.deal_vision_result(project_name, timeout=10).decode()))

    def get_async_data(self, project_name, vision_result):
        """Rewrite the business logic of this function"""
        print(project_name)
        print(vision_result)

    def deal_vision_result(self, project_name, timeout=10):
        with self.async_vision_lock:
            future = self.async_vision_future.pop(project_name, None)
        if not future:
            print("VISION_NOT_RUN_YET")
            return "{}".encode()
        e = future.exception()
        if e:
            print("VISION_NOT_REGISTERED")
            logging.error(e)
            return "{}".encode()
        vision_result = future.result(timeout=timeout)
        logging.info("Vision project name:{}, vision result:{}".format(project_name, vision_result))
        return vision_result


if __name__ == '__main__':
    a = adapter()
    a.async_call_vision_run("Vis_Station Two")
    a.async_call_vision_run("Vis_Station Three")
    """
    "async_call_vision_run" is a built-in function in the adapter, which can be found in the adapter.py file under the interface directory. Therefore, you only need to rewrite "async_get_vision_callback" and "get_async_data".
    """