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(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()) + "\\..\\..")))

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