#
# SchoolTool - common information systems platform for school administration
# Copyright (c) 2005 Shuttleworth Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
"""
Student Intervention widgets.
"""
from zope.browser.interfaces import IAdding 
from zope.formlib.interfaces import MissingInputError
from zope.formlib.interfaces import IBrowserWidget, IInputWidget
from zope.formlib.interfaces import InputErrors
from zope.app.form.browser.submit import Update
from zope.browserpage import ViewPageTemplateFile
from zope.component import adapts, getUtility, queryUtility
from zope.i18n import translate
from zope.interface import implements
from zope.security.proxy import removeSecurityProxy

from z3c.form.converter import BaseDataConverter
from z3c.form.interfaces import IFieldWidget, IDataConverter
from z3c.form.widget import Widget, FieldWidget

from schooltool.contact.interfaces import IContactable, IContact, IUniqueFormKey
from schooltool.contact.browser.relationship import get_relationship_title
from schooltool.course.interfaces import ISectionContainer
from schooltool.email.interfaces import IEmailUtility
from schooltool.group.interfaces import IGroupContainer
from schooltool.person.interfaces import IPerson
from schooltool.schoolyear.interfaces import ISchoolYear
from schooltool.term.interfaces import IDateManager

from schooltool.intervention import InterventionGettext as _
from schooltool.intervention import interfaces, intervention


class WidgetBase(object):
    """Base Class for all Widget Classes.

    XXX: why copy from zope.formlib.widget?"""

    implements(IBrowserWidget, IInputWidget)

    template = None
    _prefix = 'field.'
    _error = ''

    # See zope.formlib.interfaces.IWidget
    name = None
    label = property(lambda self: self.context.title)
    hint = property(lambda self: self.context.description)
    visible = True

    # See zope.formlib.interfaces.IInputWidget
    required = property(lambda self: self.context.required)

    def __init__(self, field, request):
        self.context = field
        self.request = request

    def isAddView(self):
        return IAdding.providedBy(self.context.context)

    def setRenderedValue(self, value):
        """See zope.formlib.interfaces.IWidget"""
        pass

    def setPrefix(self, prefix):
        """See zope.formlib.interfaces.IWidget"""
        self._prefix = prefix

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

    def hasInput(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return True

    def hasValidInput(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return True

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return None

    def error(self):
        """See zope.formlib.interfaces.IBrowserWidget"""
        return self._error

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


class PersonListWidget(WidgetBase):
    """Person List Widget"""

    template = ViewPageTemplateFile('templates/person_list.pt')

    def __init__(self, field, request):
        super(PersonListWidget, self).__init__(field, request)

        # set student from field, view type
        if self.isAddView():
            self.student = IPerson(field.context.context)
        else:
            self.student = IPerson(field.context)
        self.student = removeSecurityProxy(self.student)

        # establish email capability and error message
        utility = getUtility(IEmailUtility)
        self.enabled = utility.enabled()
        self.message = ""
        if not self.enabled:
            self.message = _('Email server is not enabled')
        else:
            sender = IContact(self.request.principal._person)
            if not sender.email:
                self.message = _('User has no email address')
                self.enabled = False

        # build responsible, notified and changed lists of contacts
        term = queryUtility(IDateManager).current_term
        advisors = [IContact(removeSecurityProxy(advisor))
                    for advisor in self.student.advisors]
        groups = IGroupContainer(ISchoolYear(term))
        administrators = [IContact(member)
                          for member in groups['administrators'].members]
        responsible = advisors + [contact for contact in administrators
                                  if contact not in advisors]
        for section in ISectionContainer(term).values():
            if self.student in section.members:
                for teacher in section.instructors:
                    if IContact(teacher) not in responsible:
                        responsible.append(IContact(teacher))
        notified = [IContact(self.student)]
        notified += [contact for contact in IContactable(self.student).contacts
                     if contact not in responsible]
        if self.isAddView():
            changed = []
        else:
            changed = [contact
                       for contact in field.context.at_one_time_responsible
                       if contact not in responsible + notified]

        # set choices attribute with complete list of contact choices
        self.choices = responsible + notified + changed

        # build value list according to view type, request
        valueList = self.getRequestValue()
        if not self.isAddView() and not Update in self.request:
            valueList = getattr(field.context, field.getName())

        # build final view attributes: responsible, notified and changed
        self.responsible = []
        self.notified = []
        self.changed = []
        for choice in self.choices:
            checked = ''
            if choice in valueList:
                checked = 'checked'

            value = intervention.contactName(choice)
            if choice in advisors:
                value = '%s (%s)' % (value, _('advisor'))
                if self.isAddView() and not Update in self.request:
                    checked = 'checked'

            if choice in IContactable(self.student).contacts:
                title = get_relationship_title(self.student, choice)
                value = '%s (%s)' % (value, title.lower())

            person = {
                'name': IUniqueFormKey(choice),
                'checked': checked,
                'value': value,
                'hasEmail': bool(removeSecurityProxy(choice).email),
                }

            if choice in responsible:
                self.responsible.append(person)
            elif choice in notified:
                self.notified.append(person)
            else:
                self.changed.append(person)

    def canEmail(self, person):
        return self.enabled and person['hasEmail']

    def getRequestValue(self):
        contacts = []
        for contact in self.choices:
            key = IUniqueFormKey(contact)
            if key in self.request:
                contacts.append(contact)
        return contacts

    def setPrefix(self, prefix):
        """See zope.formlib.interfaces.IWidget"""
        self._prefix = prefix
        self.name = prefix + self.context.username

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget"""
        contacts = self.getRequestValue()
        if not contacts and self.enabled:
            self._error = MissingInputError.__doc__
            raise MissingInputError(self.name, self.label, None)
        return contacts

    def hasValidInput(self):
        """See zope.formlib.interfaces.IInputWidget"""
        try:
            self.getInputValue()
            return True
        except InputErrors:
            return False


class PersonListZ3CFormWidget(Widget, PersonListWidget):
    """z3c form widget for PersonList field."""

    template = ViewPageTemplateFile('templates/f_person_list.pt')

    def __getitem__(self, attr):
        return getattr(self, attr)

    def addClass(self, klass):
        pass

    def getCSSClass(self, *args, **kw):
        pass

    def update(self):
        """See z3c.form.interfaces.IWidget."""
        self.student = IPerson(self.context)
        sender = IPerson(self.request.principal, None)
        if sender is not None:
            sender = IContact(sender)

        # establish email capability and error message
        utility = getUtility(IEmailUtility)
        self.enabled = utility.enabled()
        self.message = ""
        if not self.enabled:
            self.message = _('Email server is not enabled')
        else:
            if sender is None or not sender.email:
                self.message = _('User has no email address')
                self.enabled = False

        # build responsible, notified and changed lists of contacts
        term = queryUtility(IDateManager).current_term
        advisors = [IContact(removeSecurityProxy(advisor))
                    for advisor in self.student.advisors]
        groups = IGroupContainer(ISchoolYear(term))
        administrators = [IContact(member)
                          for member in groups['administrators'].members
                          if IContact(member) not in advisors]
        responsible = advisors + administrators
        teachers = []
        for section in ISectionContainer(term).values():
            if self.student in section.members:
                for teacher in section.instructors:
                    teacher = IContact(teacher)
                    if teacher not in responsible:
                        responsible.append(teacher)
                        teachers.append(teacher)
        notified = [IContact(self.student)]
        notified += [contact for contact in IContactable(self.student).contacts
                     if contact not in responsible]
        if self.isAddView():
            changed = []
        else:
            changed = [contact
                       for contact in self.context.at_one_time_responsible
                       if contact not in responsible + notified]

        # set choices attribute with complete list of contact choices
        self.choices = responsible + notified + changed

        # build value list according to view type, request
        valueList = self.getRequestValue()
        if not self.isAddView() and not 'SUBMIT' in self.request:
            valueList = getattr(self.context, self.field.getName())

        # build final view attributes: responsible, notified and changed
        self.responsible = []
        self.notified = []
        self.changed = []
        for choice in self.choices:
            title = ''
            checked = ''
            if choice in valueList:
                checked = 'checked'

            if choice in advisors:
                title = _('advisor')
                if self.isAddView() and not 'SUBMIT' in self.request:
                    checked = 'checked'
            elif choice in administrators:
                title = _('administrator')
            elif choice in teachers:
                title = _('teacher')
            elif choice is IContact(self.student):
                title = _('student')
            elif choice in IContactable(self.student).contacts:
                title = get_relationship_title(self.student, choice)
            elif choice is sender:
                title = _('self')
            if title:
                value = '%s (%s)' % (intervention.contactName(choice),
                                     translate(title, context=self.request).lower())
            else:
                value = intervention.contactName(choice)

            person = {
                'name': IUniqueFormKey(choice),
                'checked': checked,
                'value': value,
                'hasEmail': bool(removeSecurityProxy(choice).email),
                }

            if choice in responsible:
                self.responsible.append(person)
            elif choice in notified:
                self.notified.append(person)
            else:
                self.changed.append(person)

        # finally call the z3c base class
        super(PersonListZ3CFormWidget, self).update()

    def extract(self):
        return self.getRequestValue()

    def isAddView(self):
        return not interfaces.IInterventionMarker.providedBy(self.context)

    def render(self):
        self.update()
        return self.template()


def PersonListFieldWidget(field, request):
    """IFieldWidget factory for MyWidget."""
    widget = PersonListZ3CFormWidget(request)
    return FieldWidget(field, removeSecurityProxy(widget))


class PersonListFieldDataConverter(BaseDataConverter):
    """A PersonListField data converter."""
    adapts(interfaces.IPersonListField, IFieldWidget)
    implements(IDataConverter)

    def toFieldValue(self, value):
        """See interfaces.IDataConverter"""
        if value == []:
            return self.field.missing_value
        return value


class GoalMetWidget(WidgetBase):
    """Goal Met Widget"""

    template = ViewPageTemplateFile('templates/goal_met.pt')
    label = property(lambda self: '')

    def __init__(self, field, request):
        super(GoalMetWidget, self).__init__(field, request)
        
        if self.isAddView():
            value = False
        else:
            value = getattr(field.context, field.getName())

        if Update in self.request:
            value = self.getInputValue()

        self.goal_not_met = not value and 'checked' or None
        self.goal_met = value and 'checked' or None

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget"""
        return self.request['goal_met'] == 'Yes'
