from .encoder import medea
from .support import unwrap_object


class MedeaMapper(object):
    def __init__(self, *attrs, **json_attrs):
        self.attributes = set(attrs) | set(json_attrs)
        self.json_attrs = dict((attr, self.json_attr(attr)) for attr in attrs)
        self.json_attrs.update(json_attrs)

    def __call__(self, source):
        return self.to_json(source)

    def json_attr(self, attr):
        return attr

    def to_json(self, source):
        return self._map_json(source, None, True)

    def from_json(self, source, destination=None):
        return self._map_json(source, destination, False)

    def _map_json(self, source, destination, to_json):
        if destination is None:
            destination = {}
        source = unwrap_object(source)
        destination = unwrap_object(destination)

        attributes = self.attributes
        for attr in attributes:
            if self._hasattr(source, attr, to_json):
                value = self._getattr(source, attr, to_json)
                self._setattr(destination, attr, value, to_json)

        return destination

    def _hasattr(self, source, attr, to_json):
        if not to_json:
            attr = self.json_attrs.get(attr, attr)

        if isinstance(source, dict):
            return attr in source

        return hasattr(source, attr)

    def _getattr(self, source, attr, to_json):
        if not to_json:
            attr = self.json_attrs.get(attr, attr)

        if isinstance(source, dict):
            return source[attr]

        return getattr(source, attr)

    def _setattr(self, destination, attr, value, to_json):
        if to_json:
            attr = self.json_attrs.get(attr, attr)

        if isinstance(destination, dict):
            if to_json:
                value = medea(value)
            destination[attr] = value
        else:
            setattr(destination, attr, value)


class MedeaCamelMapper(MedeaMapper):
    def json_attr(self, attr):
        cs = []
        upper = False
        for c in attr:
            if c == '_':
                upper = True
            elif upper:
                cs.append(c.upper())
                upper = False
            else:
                cs.append(c)
        return ''.join(cs)

@@ -24,43 +24,8 @@
 dea%0A
-from .support import unwrap_object%0A
 %0A%0Acl
@@ -437,224 +437,158 @@
     
-return self._map_json(source, None, True)%0A%0A    def from_json(self, source, destination=None):%0A        return self._map_json(source, destination, False)%0A%0A    def _map_json(self, source, destination, to_json):%0A
+destination = %7B%7D%0A        for attr in self.attributes:%0A            if hasattr(source, attr):%0A                value = getattr(source, attr)%0A    
         
 if d
@@ -587,108 +587,119 @@
     
-if
+   
  dest
-ination is None:%0A            destination = %7B%7D%0A        source = unwrap_object(sourc
+_attr = self.json_attrs.get(attr, attr)%0A                destination%5Bdest_attr%5D = medea(valu
 e)%0A
+%0A
         
 dest
@@ -686,32 +686,39 @@
 value)%0A%0A        
+return 
 destination = un
@@ -716,25 +716,42 @@
 tion
- = unwrap_object(
+%0A%0A    def from_json(self, source, 
 dest
@@ -758,17 +758,17 @@
 ination)
-%0A
+:
 %0A       
@@ -845,47 +845,90 @@
     
-if self._hasattr(
+source_attr = self.json_attrs.get(attr, attr)%0A            if 
 source
-, 
+_
 attr
-, to_json)
+ in source
 :%0A  
@@ -954,43 +954,26 @@
  = s
-elf._getattr(
+ource%5B
 source
-, 
+_
 attr
-, to_json)
+%5D
 %0A   
@@ -989,14 +989,8 @@
     
-self._
 seta
@@ -1017,25 +1017,16 @@
 r, value
-, to_json
 )%0A%0A     
@@ -1060,16 +1060,12 @@
 def 
-_hasattr
+pick
 (sel
@@ -1077,54 +1077,37 @@
 urce
-, attr, to_json):%0A        if not to_json:%0A    
+):%0A        destination = %7B%7D%0A%0A
     
@@ -1110,32 +1110,38 @@
         attr
+ibutes
  = self.
 json_attrs.g
@@ -1132,229 +1132,70 @@
 elf.
-json_attrs.get(attr, attr)%0A%0A        if isinstance(source, dict):%0A            return attr in source%0A%0A        return hasattr(source, attr)%0A%0A    def _getattr(self, source, attr, to_json):%0A        if not to_json:%0A            
+attributes%0A        for attr in attributes:%0A            source_
 attr
@@ -1221,33 +1221,36 @@
 get(attr, attr)%0A
-%0A
+    
         if isins
@@ -1248,59 +1248,29 @@
  if 
-isinstance(source, dict):%0A            retur
+source_attr i
 n source
 %5Batt
@@ -1269,15 +1269,9 @@
 urce
-%5Battr%5D%0A
+:
 %0A   
@@ -1279,168 +1279,43 @@
     
-return getattr(source, attr)%0A%0A    def _setattr(self, destination, attr, value, to_json):%0A        if to_json:%0A            attr = self.json_attrs.get(attr, 
+        value = source%5Bsource_
 attr
-)%0A
+%5D
 %0A   
@@ -1323,200 +1323,68 @@
     
-if isinstance(destination, dict):%0A            if to_json:%0A                value = medea(value)%0A            destination%5Battr%5D = value%0A        else:%0A            setattr(destination, attr, value)
+        destination%5Battr%5D = value%0A        return destination
 %0A%0A%0Ac

Remove isinstance checks from mapper. Add mapper pick