import asyncio
import paraview.modules.vtkRemotingPythonAsyncCore as asyncCore
import paraview.modules.vtkRemotingServerManagerPython as serverManager
from paraview.modules.vtkRemotingServerManagerViews import vtkSMApplyController


class ParaT(object):
    """PythonApplication is a wrapper around vtkPVApplication for python that
    takes care of initializing vtkPVApplication with the event loop of asyncio
    which is required in order to allow waiting on futures while processing
    other application events."""

    __instance = None

    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = super(ParaT, cls).__new__(cls)
        return cls.__instance

    async def initialize(self, applicationName="python"):
        """initialize PVApplication with the current event loop.
        applicationName is used to identify the application and the main thread in the logs
        """
        Loop = asyncCore.vtkPythonRunLoop.GetInstance()
        App = serverManager.vtkPVPythonApplication.GetInstance()
        if Loop is None:
            Loop = asyncCore.vtkPythonRunLoop()

        Loop.AcquireRunningLoopFromPython()

        self.__Loop = Loop

        if App is None:
            App = serverManager.vtkPVPythonApplication()
            App.InitializeUsingPython(applicationName)

        self.__Application = App

        sessionId = await asyncCore.vtkPythonObservableWrapperUtilities.GetFuture(
            App.CreateBuiltinSession()
        )
        return self.__Application.GetSession(sessionId)

    async def finalize(self):
        """Shutdown the services and the associated endpoints"""
        assert self.__Application is not None
        self.__Application.Finalize()
        self.__Application = None
        self.__Loop = None


class ReactiveCommandHelper(asyncCore.vtkReactiveCommandHelper):
    """Create an async iterator out of a vtkCommand event."""

    def __init__(self, object, eventId):
        self.SetObject(object)
        self.SetEventId(eventId)

    def GetObservable(self):
        return asyncCore.vtkPythonObservableWrapperUtilities.GetIterator(
            super().GetObservable()
        )


class ApplyController(vtkSMApplyController):
    """A thin wrapper for  vtkSMApplyController"""

    def __init__(self, session):
        self._Session = session
        self._helper = None

    def __del__(self):
        self._Session = None
        self._helper = None

    def GetObservable(self):
        """Emits a value each time self.Apply() is executed"""
        if self._helper is None:
            self._helper = ReactiveCommandHelper(self, vtkSMApplyController.ApplyEvent)
        return self._helper.GetObservable()

    def Unsubscribe(self):
        """Terminate all asyn iterators created in GetObservable"""
        if self._helper is not None:
            self._helper.Unsubscribe()

    def Apply(self):
        pm = self._Session.GetProxyManager()
        super().Apply(pm)
