import logging

import timer
from spyder_kernels.console.kernelapp import SpyderKernelApp
from zmq.eventloop.ioloop import IOLoop

from .kernel import PyXLLSpyderKernel

_log = logging.getLogger(__name__)


class PyXLLSpyderKernelApp(SpyderKernelApp):
    """
    KernalApp for running code in Excel from Spyder.

    This is modified so that 'start' is non-blocking
    and instead schedules polling the IOLoop periodically,
    allowing Excel to continue working normally while the
    kernel is running.
    """

    kernel_class = PyXLLSpyderKernel

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__running = False
        self.__started = False
        self.__timer_id = None

    @property
    def is_running(self):
        return self.__running

    def __poll_ioloop(self, *args, **kwargs):
        """Runs the kernels IOLoop once and returns."""
        try:
            # If the kernel has been closed then run the event loop until it gets to the
            # stop event added by SpyderKernelApp.shutdown_request
            if self.kernel.shell.exit_now:
                _log.debug("Spyder kernel stopping (%s)" % self.connection_file)
                self.loop.start()

                timer.kill_timer(self.__timer_id)
                self.__timer_id = None
                self.__running = False
                return

            # otherwise call the event loop but stop immediately if there are no pending events
            self.loop.add_callback(self.loop.stop)
            self.loop.start()
        except Exception:
            _log.error("Error polling Spyder kernel loop", exc_info=True)

    def start(self):
        """Non-blocking start method that returns so Excel isn't blocked.
        The IOLoop is polled using a Windows timer instead of blocking.
        """
        if not self.__started:
            # Start the poller and the kernel, and create the IOLoop
            if self.poller is not None:
                self.poller.start()
            self.kernel.start()
            self.loop = IOLoop.current()
            self.__started = True

        if not self.__running:
            # set up a timer to periodically poll the zmq ioloop
            timer.set_timer(100, self.__poll_ioloop)
            self.__running = True
