__all__ = [
    'Client',
]

import nng
import nng.asyncs

from g1.bases import classes
from g1.bases import collections
from g1.bases.assertions import ASSERT

from . import utils


class Client:

    def __init__(self, request_type, response_type, wiredata):
        self._request_type = request_type
        self._response_type = response_type
        self._wiredata = wiredata
        # You may (optionally) use context to close socket on exit.
        self.socket = nng.asyncs.Socket(nng.Protocols.REQ0)
        self.m = collections.Namespace(
            **{name: _make_transceiver(self, name)
               for name in request_type.m}
        )

    __repr__ = classes.make_repr('{self.socket!r}')

    def __enter__(self):
        self.socket.__enter__()
        return self

    def __exit__(self, *args):
        return self.socket.__exit__(*args)

    async def transceive(self, request):
        with nng.asyncs.Context(ASSERT.not_none(self.socket)) as context:
            await context.send(self._wiredata.to_lower(request))
            wire_response = await context.recv()
            return self._wiredata.to_upper(self._response_type, wire_response)


def _make_transceiver(self, name):

    make = self._request_type.m[name]

    async def transceive(**kwargs):
        response = await self.transceive(
            self._request_type(args=make(**kwargs))
        )
        if response.error is not None:
            raise utils.select(response.error)[1]
        else:
            return getattr(response.result, name)

    return transceive

@@ -271,334 +271,254 @@
 elf.
-_request_type = request_type%0A        self._response_type = response_type%0A        self._wiredata = wiredata%0A        # You may (optionally) use context to close socket on exit.%0A        self.socket = nng.asyncs.Socket(nng.Protocols.REQ0)%0A        self.m = collections.Namespace(%0A            **%7Bname: _make_transceiver(self, nam
+socket = nng.asyncs.Socket(nng.Protocols.REQ0)%0A        self.transceive = Transceiver(self.socket, response_type, wiredata)%0A        self.m = collections.Namespace(%0A            **%7B%0A                name: Method(name, request_type, self.transceiv
 e)%0A
+ 
     
@@ -554,16 +554,29 @@
 t_type.m
+%0A            
 %7D%0A      
@@ -790,32 +790,217 @@
 s)%0A%0A
-    async def transceive
+%0Aclass Transceiver:%0A%0A    def __init__(self, socket, response_type, wiredata):%0A        self._socket = socket%0A        self._response_type = response_type%0A        self._wiredata = wiredata%0A%0A    async def __call__
 (sel
@@ -1061,24 +1061,25 @@
 t_none(self.
+_
 socket)) as 
@@ -1286,81 +1286,184 @@
 )%0A%0A%0A
-def _make_transceiver(self, name):%0A%0A    make = self._request_type.m%5Bname%5D
+class Method:%0A%0A    def __init__(self, name, request_type, transceive):%0A        self._name = name%0A        self._request_type = request_type%0A        self._transceive = transceive
 %0A%0A  
@@ -1474,27 +1474,31 @@
 ync def 
-transceive(
+__call__(self, 
 **kwargs
@@ -1526,24 +1526,25 @@
  await self.
+_
 transceive(%0A
@@ -1578,27 +1578,85 @@
 ype(
-args=make(**kwargs)
+%0A                args=self._request_type.m%5Bself._name%5D(**kwargs)%0A            
 )%0A  
@@ -1814,33 +1814,16 @@
 lt, 
-name)%0A%0A    return transceive
+self._name)
 %0A

Break Client class into smaller classes for extending method interface