from __future__ import absolute_import, unicode_literals
import time
import copy
import six

from .fields import BaseField, StringField, IntegerField, ImageField
from .fields import VoiceField, VideoField, MusicField, ArticlesField
from .fields import FieldDescriptor
from .messages import BaseMessage
from .utils import to_text, to_binary


REPLY_TYPES = {}


def register_reply(reply_type):
    def register(cls):
        REPLY_TYPES[reply_type] = cls
        return cls
    return register


class ReplyMetaClass(type):
    """Metaclass for all repies"""
    def __new__(cls, name, bases, attrs):
        for b in bases:
            if not hasattr(b, '_fields'):
                continue

            for k, v in b.__dict__.items():
                if k in attrs:
                    continue
                if isinstance(v, FieldDescriptor):
                    attrs[k] = copy.deepcopy(v.field)

        cls = super(ReplyMetaClass, cls).__new__(cls, name, bases, attrs)
        cls._fields = {}

        for name, field in cls.__dict__.items():
            if isinstance(field, BaseField):
                field.add_to_class(cls, name)
        return cls


class BaseReply(six.with_metaclass(ReplyMetaClass)):
    source = StringField('FromUserName')
    target = StringField('ToUserName')
    time = IntegerField('CreateTime', int(time.time()))
    type = 'unknown'

    def __init__(self, **kwargs):
        self._data = {}
        message = kwargs.pop('message', None)
        if message and isinstance(message, BaseMessage):
            if 'source' not in kwargs:
                kwargs['source'] = message.target
            if 'target' not in kwargs:
                kwargs['target'] = message.source
            if hasattr(message, 'agent') and 'agent' not in kwargs:
                kwargs['agent'] = message.agent
        if 'time' not in kwargs:
            kwargs['time'] = int(time.time())
        for name, value in kwargs.items():
            field = self._fields.get(name)
            if field:
                self._data[field.name] = value
            else:
                raise AttributeError('Unknown argument: {arg}'.format(
                    arg=name
                ))

    def render(self):
        tpl = '<xml>\n{data}\n</xml>'
        nodes = []
        msg_type = '<MsgType><![CDATA[{msg_type}]]></MsgType>'.format(
            msg_type=self.type
        )
        nodes.append(msg_type)
        for name, field in self._fields.items():
            value = getattr(self, name, field.default)
            node_xml = field.to_xml(value)
            nodes.append(node_xml)
        data = '\n'.join(nodes)
        return tpl.format(data=data)

    def __str__(self):
        if six.PY2:
            return to_binary(self.render())
        else:
            return to_text(self.render())


@register_reply('text')
class TextReply(BaseReply):
    type = 'text'
    content = StringField('Content')


@register_reply('image')
class ImageReply(BaseReply):
    type = 'image'
    image = ImageField('Image')

    @property
    def media_id(self):
        return self.image

    @media_id.setter
    def media_id(self, value):
        self.image = value


@register_reply('voice')
class VoiceReply(BaseReply):
    type = 'voice'
    voice = VoiceField('Voice')

    @property
    def media_id(self):
        return self.voice

    @media_id.setter
    def media_id(self, value):
        self.voice = value


@register_reply('video')
class VideoReply(BaseReply):
    type = 'video'
    video = VideoField('Video', {})

    @property
    def media_id(self):
        return self.video.get('media_id', None)

    @media_id.setter
    def media_id(self, value):
        video = self.video
        video['media_id'] = value
        self.video = video

    @property
    def title(self):
        return self.video.get('title', None)

    @title.setter
    def title(self, value):
        video = self.video
        video['title'] = value
        self.video = video

    @property
    def description(self):
        return self.video.get('description', None)

    @description.setter
    def description(self, value):
        video = self.video
        video['description']
        self.video = video


@register_reply('music')
class MusicReply(BaseReply):
    type = 'music'
    music = MusicField('Music', {})

    @property
    def thumb_media_id(self):
        return self.music.get('thumb_media_id', None)

    @thumb_media_id.setter
    def thumb_media_id(self, value):
        music = self.music
        music['thumb_media_id'] = value
        self.music = music

    @property
    def title(self):
        return self.music.get('title', None)

    @title.setter
    def title(self, value):
        music = self.music
        music['title'] = value
        self.music = music

    @property
    def description(self):
        return self.music.get('description', None)

    @description.setter
    def description(self, value):
        music = self.music
        music['description'] = value
        self.music = music

    @property
    def music_url(self):
        return self.music.get('music_url', None)

    @music_url.setter
    def music_url(self, value):
        music = self.music
        music['music_url'] = value
        self.music = music

    @property
    def hq_music_url(self):
        return self.music.get('hq_music_url', None)

    @hq_music_url.setter
    def hq_music_url(self, value):
        music = self.music
        music['hq_music_url'] = value
        self.music = music


@register_reply('news')
class ArticlesReply(BaseReply):
    type = 'news'
    articles = ArticlesField('Articles', [])

    def add_article(self, article):
        if len(self.articles) == 10:
            raise AttributeError("Can't add more than 10 articles"
                                 " in an ArticlesReply")
        articles = self.articles
        articles.append(article)
        self.articles = articles


@register_reply('transfer_customer_service')
class TransferCustomerServiceReply(BaseReply):
    type = 'transfer_customer_service'


def create_reply(reply, message=None, render=False):
    r = None
    if isinstance(reply, BaseReply):
        r = reply
    elif isinstance(reply, six.string_types):
        r = TextReply(
            message=message,
            content=reply
        )
    elif isinstance(reply, (tuple, list)):
        if len(reply) > 10:
            raise AttributeError("Can't add more than 10 articles"
                                 " in an ArticlesReply")
        r = ArticlesReply(
            message=message,
            articles=reply
        )
    if r and render:
        return r.render()
    return r

@@ -66,20 +66,8 @@
 ime%0A
-import copy%0A
 impo
@@ -97,19 +97,8 @@
 port
- BaseField,
  Str
@@ -206,44 +206,8 @@
 eld%0A
-from .fields import FieldDescriptor%0A
 from
@@ -235,16 +235,34 @@
 eMessage
+, MessageMetaClass
 %0Afrom .u
@@ -451,676 +451,8 @@
 r%0A%0A%0A
-class ReplyMetaClass(type):%0A    %22%22%22Metaclass for all repies%22%22%22%0A    def __new__(cls, name, bases, attrs):%0A        for b in bases:%0A            if not hasattr(b, '_fields'):%0A                continue%0A%0A            for k, v in b.__dict__.items():%0A                if k in attrs:%0A                    continue%0A                if isinstance(v, FieldDescriptor):%0A                    attrs%5Bk%5D = copy.deepcopy(v.field)%0A%0A        cls = super(ReplyMetaClass, cls).__new__(cls, name, bases, attrs)%0A        cls._fields = %7B%7D%0A%0A        for name, field in cls.__dict__.items():%0A            if isinstance(field, BaseField):%0A                field.add_to_class(cls, name)%0A        return cls%0A%0A%0A
 clas
@@ -482,21 +482,23 @@
 taclass(
-Reply
+Message
 MetaClas

Remove ReplyMetaClass, use MessageMetaClass instead since they are the same