# -*- coding:utf-8 -*-
# -----------------------------------------------------------------------------
#  Authors: Danilo Piparo
#           Omar Zapata <Omar.Zapata@cern.ch> http://oproject.org
# -----------------------------------------------------------------------------

################################################################################
# Copyright (C) 1995-2020, Rene Brun and Fons Rademakers.                      #
# All rights reserved.                                                         #
#                                                                              #
# For the licensing terms see $ROOTSYS/LICENSE.                                #
# For the list of contributors see $ROOTSYS/README/CREDITS.                    #
################################################################################

import queue
from threading import Thread
from time import sleep as timeSleep

import ROOT.libROOTPythonizations as _lib
from ROOT._jupyroot import helpers


class IOHandler(object):
    r"""Class used to capture output from C/C++ libraries.
    >>> import sys
    >>> h = IOHandler()
    >>> h.GetStdout()
    ''
    >>> h.GetStderr()
    ''
    >>> h.GetStreamsDicts()
    (None, None)
    >>> del h
    """

    def __init__(self):
        _lib.JupyROOTExecutorHandler_Ctor()

    def __del__(self):
        _lib.JupyROOTExecutorHandler_Dtor()

    def Clear(self):
        _lib.JupyROOTExecutorHandler_Clear()

    def Poll(self):
        _lib.JupyROOTExecutorHandler_Poll()

    def InitCapture(self):
        _lib.JupyROOTExecutorHandler_InitCapture()

    def EndCapture(self):
        _lib.JupyROOTExecutorHandler_EndCapture()

    def GetStdout(self):
        return _lib.JupyROOTExecutorHandler_GetStdout()

    def GetStderr(self):
        return _lib.JupyROOTExecutorHandler_GetStderr()

    def GetStreamsDicts(self):
        out = self.GetStdout()
        err = self.GetStderr()
        outDict = {"name": "stdout", "text": out} if out != "" else None
        errDict = {"name": "stderr", "text": err} if err != "" else None
        return outDict, errDict


class Poller(Thread):
    def __init__(self):
        Thread.__init__(self, group=None, target=None, name="JupyROOT Poller Thread")
        self.daemon = True
        self.poll = True
        self.is_running = False
        self.queue = queue.Queue()

    def run(self):
        while self.poll:
            work_item = self.queue.get()
            if work_item is not None:
                function, argument = work_item
                self.is_running = True
                function(argument)
                self.is_running = False
            else:
                self.poll = False

    def Stop(self):
        if self.is_alive():
            self.queue.put(None)
            self.join()


class Runner(object):
    """Asynchrously run functions
    >>> import time
    >>> def f(code):
    ...    print(code)
    >>> p = Poller(); p.start()
    >>> r= Runner(f, p)
    >>> r.Run("ss")
    ss
    >>> r.AsyncRun("ss");time.sleep(1)
    ss
    >>> def g(msg):
    ...    time.sleep(.25)
    ...    print(msg)
    >>> r= Runner(g, p)
    >>> r.AsyncRun("Asynchronous");print("Synchronous");time.sleep(1)
    Synchronous
    Asynchronous
    >>> r.AsyncRun("Asynchronous"); print(r.HasFinished())
    False
    >>> time.sleep(1)
    Asynchronous
    >>> print(r.HasFinished())
    True
    >>> p.Stop()
    """

    def __init__(self, function, poller):
        self.function = function
        self.poller = poller

    def Run(self, argument):
        return self.function(argument)

    def AsyncRun(self, argument):
        self.poller.is_running = True
        self.poller.queue.put((self.Run, argument))

    def Wait(self):
        while self.poller.is_running:
            timeSleep(0.1)

    def HasFinished(self):
        return not self.poller.is_running


class JupyROOTDeclarer(Runner):
    """Asynchrously execute declarations
    >>> import ROOT
    >>> p = Poller(); p.start()
    >>> d = JupyROOTDeclarer(p)
    >>> d.Run("int f(){return 3;}")
    1
    >>> ROOT.f()
    3
    >>> p.Stop()
    """

    def __init__(self, poller):
        super(JupyROOTDeclarer, self).__init__(_lib.JupyROOTDeclarer, poller)


class JupyROOTExecutor(Runner):
    r"""Asynchrously execute process lines
    >>> import ROOT
    >>> p = Poller(); p.start()
    >>> d = JupyROOTExecutor(p)
    >>> d.Run('cout << "Here am I" << endl;')
    1
    >>> p.Stop()
    """

    def __init__(self, poller):
        super(JupyROOTExecutor, self).__init__(_lib.JupyROOTExecutor, poller)


def display_drawables(displayFunction):
    drawers = helpers.utils.GetDrawers()
    for drawer in drawers:
        drawer.Draw(displayFunction)


class JupyROOTDisplayer(Runner):
    """Display all canvases"""

    def __init__(self, poller):
        super(JupyROOTDisplayer, self).__init__(display_drawables, poller)


def RunAsyncAndPrint(executor, code, ioHandler, printFunction, displayFunction, silent=False, timeout=0.1):
    ioHandler.Clear()
    ioHandler.InitCapture()
    executor.AsyncRun(code)
    while not executor.HasFinished():
        ioHandler.Poll()
        if not silent:
            printFunction(ioHandler)
            ioHandler.Clear()
        if executor.HasFinished():
            break
        timeSleep(0.1)
    executor.Wait()
    ioHandler.EndCapture()


def Display(displayer, displayFunction):
    displayer.AsyncRun(displayFunction)
    displayer.Wait()
