# coding=UTF-8
from __future__ import print_function, absolute_import, division

import six


class _Required(object):
    """Only a placeholder to tell apart None default values from required fields"""


def nop(x):
    return x


def get_tag_child(elem, tag):
    return [e for e in list(elem) if e.tag == tag]


__all__ = [
    '_name', '_docs', 'ComputedField', 'ValueField', 'SimpleField',
    '_docs_with_label', 'get_tag_child', 'ChildField'
]


class BaseField(object):
    """Base field object"""

    def set(self, obj, elem):
        obj.__dict__[self.field_name] = self.find(elem)

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

    def find(self, elem):
        raise NotImplementedError('Abstract method!')


class ComputedField(BaseField):
    """
    There's no corresponding XML attribute name - value must
    be computed from element
    """

    def __init__(self, field_name, find_fun):
        super(ComputedField, self).__init__(field_name)
        self.find = find_fun


class ValueField(BaseField):
    """
    Can hide under a pick of different XML attribute names.
    Has a type, can have a default value.
    """

    def __init__(self, xml_names, field_name, field_type=nop,
                 default=_Required):
        if not isinstance(xml_names, tuple):
            xml_names = (xml_names,)
        self.xml_names = xml_names
        assert field_type is not None
        self.field_name = field_name
        self.field_type = field_type
        self.default = default

    def find(self, elem):
        return self.field_type(self._find(elem))

    def _find(self, elem):
        xmln = [xmln for xmln in self.xml_names if xmln in elem.attrib]

        if xmln:
            return elem.attrib[xmln[0]]
        else:
            if self.default is _Required:
                raise TypeError(
                    'Did not find field %s in elem tag %s, looked for names %s' % (
                        self.field_name, elem.tag, self.xml_names))
            else:
                return self.default


class SimpleField(ValueField):
    """XML attribute is the same as name, has a type and can be default"""

    def __init__(self, name, field_type=nop, default=_Required):
        super(SimpleField, self).__init__(name, name, field_type, default)


class ChildField(ComputedField):
    """
    List of other properties
    """

    def __init__(self, name, xml_tag, fun, post_exec=nop):
        super(ChildField, self).__init__(name, lambda elem: \
                                         post_exec([fun(c) for c in get_tag_child(elem, xml_tag)]))


def get_docs(elem, label):
    """Parse an XML element. Return documentation"""
    for kid in list(elem):

        if kid.tag == 'rule':
            return get_docs(kid, False)

        s = kid.text.strip().split('\n')
        return u'\n'.join([u.strip() for u in s if len(u.strip()) > 0])

    if label:
        return elem.attrib.get('label', None)


_name = SimpleField('name', six.text_type)
_docs = ComputedField('docs', lambda elem: get_docs(elem, False))
_docs_with_label = ComputedField('docs', lambda elem: get_docs(elem, True))
