from zeit.content.video.interfaces import IVideo
import grokcore.component as grok
import zope.interface
import zope.schema


class dictproperty(object):
    """Maps a property from a CMS name to a BC name, and performs type
    conversion, using an associated CMS schema field."""

    # Might be cleaner if we figured this out by ourselves, but that would be
    # mechanically finicky and doesn't seem worth the hassle. So we simply have
    # Video.properties set it for us instead.
    __name__ = None

    def __init__(self, bc_name, iface=None, fieldname=None, field=None):
        """The value is stored in the instance's data dict, under the key
        ``bc_name`` (may contain '/' to denote nested dicts).

        Pass either iface and fieldname, or for special cases a
        zope.schema.Field instance, so we can look up type conversion
        accordingly, and access CMS values that reside in adapters.
        """
        self.bc_name = bc_name
        segments = self.bc_name.split('/')
        self.path = segments[:-1]
        self.name = segments[-1]

        assert (iface and fieldname) or field
        if field:
            self.iface = None
            self.field = field
        else:
            self.iface = iface
            self.field = iface[fieldname]

    def __get__(self, instance, cls):
        if instance is None:
            return self
        data = instance.data
        for x in self.path:
            data = data.get(x, {})
        value = data[self.name]
        converter = ITypeConverter(self.field)
        return converter.to_cms(value)

    def __set__(self, instance, value):
        if self.field.readonly:
            raise AttributeError('Cannot set %s', self.bc_name)
        data = instance.data
        for x in self.path:
            data = data.setdefault(x, {})
        converter = ITypeConverter(self.field)
        data[self.name] = converter.to_bc(value)


class Video(object):
    """Converts video data between CMS and Brightcove.

    * Stores json data received from the Brightcove API in a nested dict
    * Presents it as properties with CMS names and with CMS types for reading
    * On write, those properties convert to BC names and BC types

    Can be constructed from both a CMS object and a BC API result dict.
    """

    id = dictproperty('id', field=zope.schema.TextLine(readonly=True))
    title = dictproperty('name', IVideo, 'title')
    teaserText = dictproperty('description', IVideo, 'teaserText')

    commentsAllowed = dictproperty(
        'custom_fields/allow_comments', IVideo, 'commentsAllowed')
    ressort = dictproperty('custom_fields/ressort', IVideo, 'ressort')

    def __init__(self):
        self.data = {}

    @classmethod
    def from_cms(cls, video):
        instance = cls()
        instance.data['id'] = video.brightcove_id
        adapters = {}
        for prop in instance.properties:
            if prop.field.readonly:
                continue
            wrapped = video
            if prop.iface:
                wrapped = adapters.get(prop.iface)
                if wrapped is None:
                    wrapped = prop.iface(video)
                    adapters[prop.iface] = wrapped
            setattr(instance, prop.__name__, prop.field.get(wrapped))
        return instance

    @property
    def properties(self):
        cls = type(self)
        result = []
        for propname in dir(cls):
            prop = getattr(cls, propname)
            if not isinstance(prop, dictproperty):
                continue
            prop.__name__ = propname
            result.append(prop)
        return result

    def __repr__(self):
        return '<%s.%s %s>' % (
            self.__class__.__module__, self.__class__.__name__,
            self.id or '(unknown)')


class ITypeConverter(zope.interface.Interface):

    def to_bc(value):
        pass

    def to_cms(value):
        pass


class DefaultPassthroughConverter(grok.Adapter):

    grok.context(zope.interface.Interface)
    grok.implements(ITypeConverter)

    def to_bc(self, value):
        return value

    def to_cms(self, value):
        return value


class BoolConverter(grok.Adapter):

    grok.context(zope.schema.Bool)
    grok.implements(ITypeConverter)

    def to_bc(self, value):
        return '1' if value else '0'

    def to_cms(self, value):
        return value == '1'

@@ -537,46 +537,13 @@
 me, 
-iface=None, fieldname=None, field=None
+field
 ):%0A 
@@ -681,576 +681,187 @@
 s).%0A
-%0A        Pass either iface and fieldname, or for special cases a%0A        zope.schema.Field instance, so we can look up type conversion%0A        accordingly, and access CMS values that reside in adapters.%0A        %22%22%22%0A        self.bc_name = bc_name%0A        segments = self.bc_name.split('/')%0A        self.path = segments%5B:-1%5D%0A        self.name = segments%5B-1%5D%0A%0A        assert (iface and fieldname) or field%0A        if field:%0A            self.iface = None%0A            self.field = field%0A        else:%0A            self.iface = iface%0A            self.field = iface%5Bfieldname%5D
+        %22%22%22%0A        self.bc_name = bc_name%0A        segments = self.bc_name.split('/')%0A        self.path = segments%5B:-1%5D%0A        self.name = segments%5B-1%5D%0A        self.field = field
 %0A%0A  
@@ -1901,14 +1901,8 @@
 d', 
-field=
 zope
@@ -1977,17 +1977,17 @@
 ideo
-, 
+%5B
 'title'
+%5D
 )%0A  
@@ -2035,18 +2035,17 @@
 , IVideo
-, 
+%5B
 'teaserT
@@ -2048,16 +2048,17 @@
 serText'
+%5D
 )%0A%0A    c
@@ -2134,18 +2134,17 @@
 , IVideo
-, 
+%5B
 'comment
@@ -2152,16 +2152,17 @@
 Allowed'
+%5D
 )%0A    re
@@ -2213,18 +2213,17 @@
 , IVideo
-, 
+%5B
 'ressort
@@ -2223,16 +2223,17 @@
 ressort'
+%5D
 )%0A%0A    d
@@ -2401,30 +2401,8 @@
 _id%0A
-        adapters = %7B%7D%0A
     
@@ -2503,249 +2503,8 @@
 nue%0A
-            wrapped = video%0A            if prop.iface:%0A                wrapped = adapters.get(prop.iface)%0A                if wrapped is None:%0A                    wrapped = prop.iface(video)%0A                    adapters%5Bprop.iface%5D = wrapped%0A
     
@@ -2563,15 +2563,13 @@
 get(
-wrapped
+video
 ))%0A 

Remove adapting to interface, it turns out unnecessary