"""
Thread Yönetimi ve Asynchronous İşlemler

CATIA COM nesneleri thread'ler arası kullanılırken special marshalling
mekanizması gerektiriyor. Bu modül @Async() decorator'u kullanarak
işlemleri arka planda çalıştırırken thread safety sağlar.
"""

import threading
import pythoncom
from functools import wraps


class AsyncMethod(object):
    """
    Asynchronous metodu çalıştıran sınıf (Descriptor)

    Uzun süren işlemleri arka planda (thread'de) çalıştırıp,
    UI thread'ini bloke etmeden callback'i çağırır.

    Instance metotları (self parametresi olan) için uygun.
    """

    def __init__(self, fnc, callback=None):
        """
        Args:
            fnc: Çalıştırılacak fonksiyon
            callback: İşlem tamamlandığında çağrılacak fonksiyon (signal emit'i)
        """
        self.fnc = fnc
        self.callback = callback
        wraps(fnc)(self)

    def __get__(self, obj, objtype=None):
        """
        Descriptor protokolü - instance metot olarak çalışması için
        """
        if obj is None:
            return self

        # Bound metot döndür
        import functools
        return functools.partial(self.__call__, obj)

    def __call__(self, obj, *args, **kwargs):
        """
        Asynchronous metodu çalıştır (self=obj parametre olarak alır)

        COM nesneleri (CDispatch) stream olarak dönüştürülerek
        thread'ler arası aktarılır.
        """
        # Argümanları işle - COM nesneklerini marshal et
        argler = []
        for x in range(len(args)):
            arg_type = str(type(args[x]))

            # COM nesnesi kontrolü (win32com.client.CDispatch)
            if "<class 'win32com.client.CDispatch'>" in arg_type:
                try:
                    # COM nesnesini inter-thread stream'e çevir
                    marshallanmis = pythoncom.CoMarshalInterThreadInterfaceInStream(
                        pythoncom.IID_IDispatch, args[x]
                    )
                    argler.append(marshallanmis)
                except Exception as e:
                    print(f"COM marshalling hatası: {e}")
                    argler.append(args[x])
            else:
                # Normal Python nesnesi
                argler.append(args[x])

        # Thread'de çalıştırılacak wrapper fonksiyonu
        def ThreadProc():
            try:
                # COM context'ini başlat
                pythoncom.CoInitialize()

                # Marshal edilen COM nesneklerini geri çevir
                gercek_argler = []
                for arg in argler:
                    if isinstance(arg, bytes):
                        # Marshal stream'den geri çevir
                        try:
                            com_obj = pythoncom.CoGetInterfaceAndReleaseStream(
                                arg, pythoncom.IID_IDispatch
                            )
                            gercek_argler.append(
                                __import__('win32com.client', fromlist=['Dispatch']).Dispatch(com_obj)
                            )
                        except Exception as e:
                            print(f"Unmarshal hatası: {e}")
                            gercek_argler.append(arg)
                    else:
                        gercek_argler.append(arg)

                # Asynchronous metodu çalıştır (self=obj ile)
                sonuc = self.fnc(obj, *gercek_argler, **kwargs)

                # Callback'i çağır (genellikle signal emit'i)
                if self.callback:
                    self.callback(sonuc)

            except Exception as e:
                print(f"Async işlem hatası: {e}")
                if self.callback:
                    self.callback(None)
            finally:
                # COM context'ini temizle
                pythoncom.CoUninitialize()

        # Thread'i background'da çalıştır
        thread = threading.Thread(target=ThreadProc, daemon=True)
        thread.start()


def Async(fnc=None, callback=None):
    """
    Decorator: @Async() ile asynchronous işlem yapabilme

    Kullanım:
        @Async()
        def uzunGorev(fikatia, sinyal=self.sinyal):
            # İşlem yap
            sinyal.emit("Tamamlandı", {})

    Args:
        fnc: Süslenecek fonksiyon (decorator @Async() olarak kullanılırsa None)
        callback: Tamamlanıldığında çağrılacak fonksiyon
    """
    if fnc is None:
        # @Async() olarak kullanıldı
        def AddAsyncCallback(fnc):
            return AsyncMethod(fnc, callback)
        return AddAsyncCallback
    else:
        # @Async hiç argüman olmadan kullanıldı
        return AsyncMethod(fnc, callback)
