# Copyright (c) 2007-2009, Linden Research, Inc.
# Copyright (c) 2007, IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys

from eventlet import event
from eventlet import greenio
from eventlet import greenthread
from eventlet import patcher
threading = patcher.original('threading')
Queue_module = patcher.original('Queue')
Queue = Queue_module.Queue
Empty = Queue_module.Empty

__all__ = ['execute', 'Proxy', 'killall']

QUIET=True

_rfile = _wfile = None

_bytetosend = ' '.encode()

def _signal_t2e():
    _wfile.write(_bytetosend)
    _wfile.flush()

_reqq = None
_rspq = None

def tpool_trampoline():
    global _reqq, _rspq
    while(True):
        try:
            _c = _rfile.read(1)
            assert _c
        except ValueError:
            break  # will be raised when pipe is closed
        while not _rspq.empty():
            try:
                (e,rv) = _rspq.get(block=False)
                e.send(rv)
                rv = None
            except Empty:
                pass

def esend(meth,*args, **kwargs):
    global _reqq, _rspq
    e = event.Event()
    _reqq.put((e,meth,args,kwargs))
    return e

SYS_EXCS = (KeyboardInterrupt, SystemExit)


def tworker():
    global _reqq, _rspq
    while(True):
        try:
            msg = _reqq.get()
        except AttributeError:
            return # can't get anything off of a dud queue
        if msg is None:
            return
        (e,meth,args,kwargs) = msg
        rv = None
        try:
            rv = meth(*args,**kwargs)
        except SYS_EXCS:
            raise
        except Exception:
            rv = sys.exc_info()
        _rspq.put((e,rv))               # @@tavis: not supposed to
                                        # keep references to
                                        # sys.exc_info() so it would
                                        # be worthwhile testing
                                        # if this leads to memory leaks
        meth = args = kwargs = e = rv = None
        _signal_t2e()


def erecv(e):
    rv = e.wait()
    if isinstance(rv,tuple) and len(rv) == 3 and isinstance(rv[1],Exception):
        import traceback
        (c,e,tb) = rv
        if not QUIET:
            traceback.print_exception(c,e,tb)
            traceback.print_stack()
        raise c,e,tb
    return rv


def execute(meth,*args, **kwargs):
    """
    Execute *meth* in a Python thread, blocking the current coroutine/
    greenthread until the method completes.

    The primary use case for this is to wrap an object or module that is not
    amenable to monkeypatching or any of the other tricks that Eventlet uses
    to achieve cooperative yielding.  With tpool, you can force such objects to
    cooperate with green threads by sticking them in native threads, at the cost
    of some overhead.
    """
    global _threads
    setup()
    my_thread = threading.currentThread()
    if my_thread in _threads:
        return meth(*args, **kwargs)
    e = esend(meth, *args, **kwargs)