# Slixmpp: The Slick XMPP Library
# Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
# This file is part of Slixmpp.
# See the file LICENSE for copying permission.
import logging

from slixmpp.xmlstream import ElementBase, ET


class FormField(ElementBase):
    namespace = 'jabber:x:data'
    name = 'field'
    plugin_attrib = 'field'
    plugin_multi_attrib = 'fields'
    interfaces = {'answer', 'desc', 'required', 'value',
                  'label', 'type', 'var'}
    sub_interfaces = {'desc'}
    plugin_tag_map = {}
    plugin_attrib_map = {}

    field_types = {'boolean', 'fixed', 'hidden', 'jid-multi',
                   'jid-single', 'list-multi', 'list-single',
                   'text-multi', 'text-private', 'text-single'}

    true_values = {True, '1', 'true'}
    option_types = {'list-multi', 'list-single'}
    multi_line_types = {'hidden', 'text-multi'}
    multi_value_types = {'hidden', 'jid-multi',
                         'list-multi', 'text-multi'}

    def setup(self, xml=None):
        if ElementBase.setup(self, xml):
            self._type = None
        else:
            self._type = self['type']

    def set_type(self, value):
        self._set_attr('type', value)
        if value:
            self._type = value

    def add_option(self, label='', value=''):
        if self._type is None or self._type in self.option_types:
            opt = FieldOption()
            opt['label'] = label
            opt['value'] = value
            self.append(opt)
        else:
            raise ValueError("Cannot add options to " + \
                             "a %s field." % self['type'])

    def del_options(self):
        optsXML = self.xml.findall('{%s}option' % self.namespace)
        for optXML in optsXML:
            self.xml.remove(optXML)

    def del_required(self):
        reqXML = self.xml.find('{%s}required' % self.namespace)
        if reqXML is not None:
            self.xml.remove(reqXML)

    def del_value(self):
        valsXML = self.xml.findall('{%s}value' % self.namespace)
        for valXML in valsXML:
            self.xml.remove(valXML)

    def get_answer(self):
        return self['value']

    def get_options(self):
        options = []
        optsXML = self.xml.findall('{%s}option' % self.namespace)
        for optXML in optsXML:
            opt = FieldOption(xml=optXML)
            options.append({'label': opt['label'], 'value': opt['value']})
        return options

    def get_required(self):
        reqXML = self.xml.find('{%s}required' % self.namespace)
        return reqXML is not None

    def get_value(self, convert=True, convert_list=False):
        """
        Gets the value for this field

        :param convert: Convert truthy values to boolean
        :param convert_list: Convert text-multi fields to a string with
            \n as separator for values
        """
        valsXML = self.xml.findall('{%s}value' % self.namespace)
        if len(valsXML) == 0:
            return None
        elif self._type == 'boolean':
            if convert:
                return valsXML[0].text in self.true_values
            return valsXML[0].text
        elif self._type in self.multi_value_types or len(valsXML) > 1:
            values = []
            for valXML in valsXML:
                if valXML.text is None:
                    valXML.text = ''
                values.append(valXML.text)
            if self._type == 'text-multi' and convert_list:
                values = "\n".join(values)
            return values
        else:
            if valsXML[0].text is None:
                return ''
            return valsXML[0].text

    def set_answer(self, answer):
        self['value'] = answer

    def set_false(self):
        self['value'] = False

    def set_options(self, options):
        for value in options:
            if isinstance(value, dict):
                self.add_option(**value)
            else:
                self.add_option(value=value)

    def set_required(self, required):
        exists = self['required']
        if not exists and required:
            self.xml.append(ET.Element('{%s}required' % self.namespace))
        elif exists and not required:
            del self['required']

    def set_true(self):
        self['value'] = True

    def set_value(self, value):
        del self['value']
        valXMLName = '{%s}value' % self.namespace

        if not self._type:
            if isinstance(value, bool):
                log.debug("Passed a 'boolean' as value of an untyped field, assuming it is a 'boolean'")
                self._type = "boolean"
            elif isinstance(value, str):
                log.debug("Passed a 'str' as value of an untyped field, assuming it is a 'text-single'")
                self._type = "text-single"
            elif isinstance(value, (list, tuple)):
                log.debug("Passed a %s as value of an untyped field, assuming it is a 'text-multi'")
                self._type = "text-multi"

        if self._type == 'boolean':
            if value in self.true_values:
                valXML = ET.Element(valXMLName)
                valXML.text = '1'
                self.xml.append(valXML)
            else:
                valXML = ET.Element(valXMLName)
                valXML.text = '0'
                self.xml.append(valXML)
        elif self._type in self.multi_value_types or self._type in ('', None):
            if isinstance(value, bool):
                value = [value]
            if not isinstance(value, list):
                value = value.replace('\r', '')
                value = value.split('\n')
            for val in value:
                if self._type in ('', None) and val in self.true_values:
                    val = '1'
                valXML = ET.Element(valXMLName)
                valXML.text = val
                self.xml.append(valXML)
        else:
            if isinstance(value, list):
                raise ValueError("Cannot add multiple values " + \
                                 "to a %s field." % self._type)
            valXML = ET.Element(valXMLName)
            valXML.text = value
            self.xml.append(valXML)


class FieldOption(ElementBase):
    namespace = 'jabber:x:data'
    name = 'option'
    plugin_attrib = 'option'
    interfaces = {'label', 'value'}
    sub_interfaces = {'value'}
    plugin_multi_attrib = 'options'


FormField.addOption = FormField.add_option
FormField.delOptions = FormField.del_options
FormField.delRequired = FormField.del_required
FormField.delValue = FormField.del_value
FormField.getAnswer = FormField.get_answer
FormField.getOptions = FormField.get_options
FormField.getRequired = FormField.get_required
FormField.getValue = FormField.get_value
FormField.setAnswer = FormField.set_answer
FormField.setFalse = FormField.set_false
FormField.setOptions = FormField.set_options
FormField.setRequired = FormField.set_required
FormField.setTrue = FormField.set_true
FormField.setValue = FormField.set_value


log = logging.getLogger(__name__)
