"""A phone number widget.

This widget is an input widget (not made for display forms but for
edit and add forms).

It can be used for :class:`zope.schema.TextLine` fields but has to be
requested by the form manually (otherwise the regular TextLine widget
will be used for rendering).

The code was mainly taken from usphone implementation in z3c.widget
(which was designed for outdated zope.app.form (instead of
zope.formlib or newer form libs). Therefore some modifications were
neccessary and it might look a bit overloaded.

If you use the PhoneWidget for rendering regular TextLine attributes
(preferably in edit forms or add forms), the phone number is displayed
by three input fields representing the international code, the area
code and the extension line. All three fields require pure numbers as
input and do not accept other chars.

When the entered input is stored with a context object, it is stored
as a single unicode string with the numbers divided by single hyphen.

So, input <12>, <111>, <444> becomes the string ``'12-111-444'`` for
the context object.

Large parts of this module are copied from z3c.widget and may be
covered by ZPL.

"""
import re
import copy
from waeup.sirp.interfaces import MessageFactory as _
from zope import schema
from zope.browserpage import ViewPageTemplateFile
from zope.component import getMultiAdapter
from zope.formlib import form
from zope.formlib.interfaces import (
    IBrowserWidget, IWidgetInputErrorView, IInputWidget, WidgetInputError,
    MissingInputError)
from zope.formlib.widget import SimpleInputWidget
from zope.interface import Interface, implements
from zope.schema.interfaces import RequiredMissing

class IPhoneData(Interface):
    """A schema used to generate a Phone widget."""

    country = schema.TextLine(
        title=u'Country Code',
        description=u'The country code of the phone number.',
        min_length=1,
        constraint=re.compile(r'^[0-9]+$').search,
        default=u'234', # int. code for nigeria
        required=True)

    area = schema.TextLine(
        title=u'Area Code',
        description=u'The area code of the phone number.',
        min_length=1,
        constraint=re.compile(r'^[0-9]+$').search,
        required=True)

    extension = schema.TextLine(
        title=u'Direct Line',
        description=u'The direct line of the phone number.',
        min_length=3,
        constraint=re.compile(r'^[0-9]{3,}$').search,
        required=True)

class PhoneWidgetData(object):
    """Phone number data"""
    implements(IPhoneData)

    country = None
    area = None
    extension = None

    def __init__(self, context, number=None):
        """Set countrycode, areacode, and extension line from number.
        """
        self.context = context
        if number is None:
            return
        # handle other types than strings.
        if not isinstance(number, basestring):
            number = str(number)
        parts = number.split('-', 2)
        if len(parts) == 2:
            parts = [None,] + parts
        elif len(parts) == 0:
            return
        elif len(parts) == 1:
            parts = [None, None] + parts
        self.country, self.area, self.extension = parts
        return

    @property
    def number(self):
        """Return a valid phone number string of format ``<IC>-<AC>-<EL>``.

        where <IC> is the country code (digits only), <AC> is the area
        code and <EL> is the extension line.
        """
        return u'-'.join(
            (self.country or '', self.area or '', self.extension))


class PhoneWidget(SimpleInputWidget):
    """Phone Number Widget"""
    implements(IBrowserWidget, IInputWidget)

    template = ViewPageTemplateFile('phonewidget.pt')
    _prefix = 'field.'
    _error = None
    widgets = {}

    # See zope.formlib.interfaces.IWidget
    name = None
    visible = True

    def __init__(self, field, request):
        super(PhoneWidget, self).__init__(field, request)
        value = field.query(field.context)
        self._generateSubWidgets()
        return

    def _generateSubWidgets(self):
        """Create the three subwidgets.
        """
        value = self.context.query(self.context.context)
        # We have to modify the adapter according to our own
        # 'required' state. Some ugly workaround
        adapter = copy.deepcopy(IPhoneData)
        for name in 'country', 'area', 'extension':
            adapter[name].required = self.required
        adapters = {adapter: PhoneWidgetData(self, value)}
        self.widgets = form.setUpEditWidgets(
            form.FormFields(IPhoneData),
            self.name, value, self.request, adapters=adapters)
        # The displayWidth cant'be set
        # because it is overridden by Bootstrap css classes
        #self.widgets['country'].displayWidth = 3
        #self.widgets['area'].displayWidth = 5
        #self.widgets['extension'].displayWidth = 10
        # We have to set the cssClass attributes
        self.widgets['country'].cssClass = 'span2'
        self.widgets['area'].cssClass = 'span2'
        self.widgets['extension'].cssClass = 'span3'

        return

    def setRenderedValue(self, value):
        """See zope.formlib.interfaces.IWidget"""
        if isinstance(value, unicode) and '-' in value:
            country, area, extension = value.split('-', 2)
            self.widgets['country'].setRenderedValue(country)
            self.widgets['area'].setRenderedValue(area)
            self.widgets['extension'].setRenderedValue(extension)
            return
        return

    def setPrefix(self, prefix):
        """See zope.formlib.interfaces.IWidget"""
        # Set the prefix locally
        if not prefix.endswith("."):
            prefix += '.'
        self._prefix = prefix
        self.name = prefix + self.context.__name__
        # Now distribute it to the sub-widgets
        self._generateSubWidgets()
        return

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget"""
        self._error = None
        try:
            result = u'-'.join((
                self.widgets['country'].getInputValue(),
                self.widgets['area'].getInputValue(),
                self.widgets['extension'].getInputValue() ))
            return result
        except TypeError:
            if self.required:
                return self._missing
            return None
        except ValueError, v:
            self._error = WidgetInputError(
                self.context.__name__, self.label, _(v))
            raise self._error
        except WidgetInputError, e:
            self._error = e
            raise e

    def applyChanges(self, content):
        """See zope.formlib.interfaces.IInputWidget"""
        field = self.context
        new_value = self.getInputValue()
        old_value = field.query(content, self)
        # The selection has not changed
        if new_value == old_value:
            return False
        field.set(content, new_value)
        return True

    def hasInput(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return (self.widgets['country'].hasInput() and
                (self.widgets['area'].hasInput() and
                 self.widgets['extension'].hasInput()))


    def hasValidInput(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return (self.widgets['country'].hasValidInput() and
            self.widgets['area'].hasValidInput() and
            self.widgets['extension'].hasValidInput())


    def hidden(self):
        """See zope.formlib.interfaces.IBrowserWidget"""
        output = []
        output.append(self.widgets['country'].hidden())
        output.append(self.widgets['area'].hidden())
        output.append(self.widgets['extension'].hidden())
        return '\n'.join(output)


    def error(self):
        """See zope.formlib.interfaces.IBrowserWidget"""
        if self._error:
            return getMultiAdapter(
                (self._error, self.request),
                IWidgetInputErrorView).snippet()
        country_error = self.widgets['country'].error()
        if country_error:
            return country_error
        area_error = self.widgets['area'].error()
        if area_error:
            return area_error
        extension_error = self.widgets['extension'].error()
        if extension_error:
            return extension_error
        return ""

    def __call__(self):
        """See zope.formlib.interfaces.IBrowserWidget"""
        return self.template()
