""" transparent.py - Several transparent proxy helpers
"""
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.typedef import Function, GeneratorIterator, PyTraceback, \
    PyFrame, PyCode
from pypy.objspace.std.proxyobject import W_Transparent
from pypy.objspace.std.typeobject import W_TypeObject
from rpython.rlib.unroll import unrolling_iterable


class W_TransparentFunction(W_Transparent):
    typedef = Function.typedef

class W_TransparentTraceback(W_Transparent):
    typedef = PyTraceback.typedef

class W_TransparentCode(W_Transparent):
    typedef = PyCode.typedef

class W_TransparentFrame(W_Transparent):
    typedef = PyFrame.typedef

class W_TransparentGenerator(W_Transparent):
    typedef = GeneratorIterator.typedef


class TypeCache(object):
    def __init__(self):
        self.cache = []

    def _freeze_(self):
        self.cache = unrolling_iterable(self.cache)
        return True

type_cache = TypeCache()


def setup(space):
    """Add proxy functions to the __pypy__ module."""
    w___pypy__ = space.getbuiltinmodule("__pypy__")
    space.setattr(w___pypy__, space.newtext('tproxy'), app_proxy.spacebind(space))
    space.setattr(w___pypy__, space.newtext('get_tproxy_controller'),
                  app_proxy_controller.spacebind(space))


def proxy(space, w_type, w_controller):
    """tproxy(typ, controller) -> obj
Return something that looks like it is of type typ. Its behaviour is
completely controlled by the controller."""
    if not space.is_true(space.callable(w_controller)):
        raise oefmt(space.w_TypeError, "controller should be function")

    if isinstance(w_type, W_TypeObject):
        if space.issubtype_w(w_type, space.gettypeobject(Function.typedef)):
            return W_TransparentFunction(space, w_type, w_controller)
        if space.issubtype_w(w_type, space.gettypeobject(PyTraceback.typedef)):
            return W_TransparentTraceback(space, w_type, w_controller)
        if space.issubtype_w(w_type, space.gettypeobject(PyFrame.typedef)):
            return W_TransparentFrame(space, w_type, w_controller)
        if space.issubtype_w(w_type, space.gettypeobject(GeneratorIterator.typedef)):
            return W_TransparentGenerator(space, w_type, w_controller)
        if space.issubtype_w(w_type, space.gettypeobject(PyCode.typedef)):
            return W_TransparentCode(space, w_type, w_controller)
        if w_type.layout.typedef is space.w_object.layout.typedef:
            return W_Transparent(space, w_type, w_controller)
    else:
        raise oefmt(space.w_TypeError, "type expected as first argument")
    w_lookup = w_type
    for k, v in type_cache.cache:
        if w_lookup == k:
            return v(space, w_type, w_controller)
    raise oefmt(space.w_TypeError, "'%N' object could not be wrapped", w_type)

def register_proxyable(space, cls):
    tpdef = cls.typedef
    class W_TransparentUserCreated(W_Transparent):
        typedef = tpdef
    type_cache.cache.append((space.gettypeobject(tpdef), W_TransparentUserCreated))

def proxy_controller(space, w_object):
    """get_tproxy_controller(obj) -> controller
If obj is really a transparent proxy, return its controller. Otherwise return
None."""
    if isinstance(w_object, W_Transparent):
        return w_object.w_controller
    #if isinstance(w_object, W_TransparentObject):
    #    return w_object.w_controller
    return None

app_proxy = gateway.interp2app(proxy)
app_proxy_controller = gateway.interp2app(proxy_controller)
