# Module name clash - "json" is the current module as well as
# a stdlib module. Use importlib to force importing
# the latter.
import importlib
json = importlib.import_module('json', '')

from amqpservices.serializers import base
from amqpservices import Server, Client
from amqpservices.base import LOCALHOST_CONNECTION


class JSONSerializer(base.Serializer):

    def from_bytes(self, bs):
        return json.loads(bs)

    def to_bytes(self, p):
        return json.dumps(p)


def expose(f):
    f.amqpservices_exposed = True
    return f

def is_exposed(f):
    return getattr(f, 'amqpservices_exposed', False)


class JSONDispatcher(base.ResponseProducer):

    def __init__(self, impl):
        self.impl = impl

    def __call__(self, properties, json_request):
        method_name, args, kwargs = json_request['method'], \
            json_request.get('args', []), \
            json_request.get('kwargs', {})
        method = getattr(self.impl, method_name)
        if not is_exposed(method):
            raise AttributeError('Method %s is not exposed' % method_name)
        result = method(*args, **kwargs)
        return result


class JSONFromListDispatcher(base.ResponseProducer):

    def __init__(self, method_list):
        self.method_list = method_list
        self._name_to_method = {m.__name__: m for m in self.method_list}

    def __call__(self, properties, json_request):
        method_name, args, kwargs = json_request['method'], \
            json_request.get('args', []), \
            json_request.get('kwargs', {})
        if method_name not in self._name_to_method:
            raise AttributeError('Method not found: %s' % method_name)
        result = self._name_to_method[method_name](*args, **kwargs)
        return result


class _MethodProxy(object):

    def __init__(self, proxy, name):
        self.proxy = proxy
        self.name = name

    def __call__(self, *args, **kwargs):
        request = {
                'method': self.name,
                'args': args,
                'kwargs': kwargs,
                }
        return self.proxy.client.send_request(self.proxy.routing_key, request)


class JSONCallerProxy(object):

    def __init__(self, client, routing_key):
        self.client = client
        self.routing_key = routing_key

    def __getattr__(self, name):
        return _MethodProxy(self, name)


def json_server(service, impl):
    return Server(service=service,
            serializer=JSONSerializer(),
            callback=JSONDispatcher(impl))

def json_methods_server(service, fun_list):
    return Server(service=service,
            serializer=JSONSerializer(),
            callback=JSONFromListDispatcher(fun_list))

def json_proxy(routing_key, connparams=LOCALHOST_CONNECTION):
    client = Client(connparams=connparams, serializer=JSONSerializer())
    return JSONCallerProxy(client, routing_key)

