from copy import copy
import inspect
import os
from os.path import join, abspath
import threading
import pythoncom
import sys
from win32com.client import Dispatch

sys.path.append(join(abspath("."), "FikatiaV06"))
print(join(abspath("."), "FikatiaV06"))

from IFikatia import IFikatia
from IFikomObject import IFikomObject


class AsyncCall(object):
    def __init__(self, fnc, callback=None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        self.Thread = threading.Thread(target=self.run, name=self.Callable.__name__, args=args, kwargs=kwargs)
        self.Thread.setDaemon(True)
        self.Thread.start()
        return self

    def wait(self, timeout=None):
        self.Thread.join(timeout)
        if self.Thread.isAlive():
            raise TimeoutError()
        else:
            return self.Result

    def run(self, *args, **kwargs):
        pythoncom.CoInitialize()
        argler = []
        arglerAppend = argler.append
        for x in range(len(args)):
            if str(type(args[x])) == "<class 'PyIStream'>":
                arglerAppend(Dispatch(pythoncom.CoGetInterfaceAndReleaseStream(args[x], pythoncom.IID_IDispatch)))
            else:
                arglerAppend(args[x])

        for x in range(len(argler)):
            if type(argler[x]) == list:
                for y in range(len(argler[x])):
                    if str(type(argler[x][y])) == "<class 'PyIStream'>":
                        argler[x][y] = Dispatch(
                            pythoncom.CoGetInterfaceAndReleaseStream(args[x][y], pythoncom.IID_IDispatch))

        self.Result = self.Callable(*argler, **kwargs)
        if self.Callback:
            self.Callback(self.Result)


class AsyncMethod(object):
    def __init__(self, fnc, callback=None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        argler = []
        for x in range(len(args)):
            if str(type(args[x])) == "<class 'win32com.client.CDispatch'>":
                argler.append(pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, args[x]))
            elif isinstance(args[x], IFikomObject):
                argler.append(
                        pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, args[x].catNesne))
            else:
                argler.append(args[x])

        for x in range(len(argler)):
            if type(argler[x]) == list:
                for y in range(len(argler[x])):
                    if str(type(args[x][y])) == "<class 'win32com.client.CDispatch'>":
                        args[x][y] = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch,
                                                                                     args[x][y])
                    elif isinstance(args[x][y], IFikomObject):
                        args[x][y] = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch,
                                                                                     args[x][y].catNesne)

        return AsyncCall(self.Callable, self.Callback)(*argler, **kwargs)


def Async(fnc=None, callback=None):
    if fnc == None:
        def AddAsyncCallback(fnc):
            return AsyncMethod(fnc, callback)

        return AddAsyncCallback
    else:
        return AsyncMethod(fnc, callback)


        # class FikoThreadManager(object):
        #     def __init__(self):
        #         self.__threadler = []
        #
        #     def ekle(self, **kwargs) -> threading.Thread:
        #
        #         _mvctThr = self.item(kwargs.get("ismi"))
        #         if _mvctThr:
        #             thread = threading.Thread(name=_mvctThr.get("ismi"), target=_mvctThr.get("func"),
        #                                       args=_mvctThr.get("params"))
        #             thread.setDaemon(_mvctThr.get("daemon"))
        #             return thread
        #
        #         thread = threading.Thread(name=kwargs.get("ismi"), target=kwargs.get("func"), args=kwargs.get("params"))
        #         self.__threadler.append(kwargs)
        #
        #         thread.setDaemon(kwargs.get("daemon"))
        #
        #         return thread
        #
        #     def item(self, ismi:str, params:tuple=None) -> threading.Thread:
        #         rslt = None
        #         for thr in self.__threadler:
        #             if thr["ismi"] == ismi:
        #                 rslt = threading.Thread(name=thr.get("ismi"), target=thr.get("func"),
        #                                         args=thr.get("params") if not params else params, daemon=thr.get("daemon"))
        #         return rslt
