#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

import pytz
from persistent import Persistent
from datetime import datetime

from zope.annotation.interfaces import IAttributeAnnotatable
from zope.authentication.interfaces import IUnauthenticatedPrincipal
from zope.container.contained import Contained, NameChooser
from zope.container.interfaces import INameChooser
from zope.container.btree import BTreeContainer
from zope.component import queryUtility
from zope.component import adapter
from zope.interface import implementer
from zope.interface import implements
from zope.location import location
from zope.publisher.interfaces import IPublishTraverse, IRequest
from zope.publisher.browser import TestRequest
from zope.security import management

from schooltool.app.app import InitBase, StartUpBase
from schooltool.app.interfaces import ISchoolToolApplication
from schooltool.contact.interfaces import IContactable, IContact
from schooltool.course.interfaces import ISectionContainer
from schooltool.group.interfaces import IGroupContainer
from schooltool.intervention import InterventionGettext as _
from schooltool.schoolyear.interfaces import ISchoolYear
from schooltool.schoolyear.interfaces import ISchoolYearContainer
from schooltool.securitypolicy import crowds
from schooltool.term.interfaces import IDateManager
from schooltool.traverser.traverser import NameTraverserPlugin

import interfaces


def getRequest():
    """Return the request object for the current request.
       In the case of unit testing, return a TestRequest instance."""

    i = management.getInteraction()
    for p in i.participations:
        if IRequest.providedBy(p):
            return p
    return TestRequest()


def convertIdToPerson(id):
    return ISchoolToolApplication(None)['persons'][id]


def convertIdToNameWithSort(id):
    if id[-2] == ':':
        student = convertIdToPerson(id[:-2])
        for index, contact in enumerate(IContactable(student).contacts):
            if id[-1] == str(index + 1):
                first_name = contact.first_name
                last_name = contact.last_name
                break
        else:
            return ('', ''), _('Unknown Contact')
    else:
        person = convertIdToPerson(id)
        first_name = person.first_name
        last_name = person.last_name
    name = '%s %s' % (first_name, last_name)
    sort = (last_name, first_name)
    return sort, name


def convertIdToName(id):
    sort, name = convertIdToNameWithSort(id)
    return name


def convertIdsToNames(ids):
    return [name for sort, name in
        sorted([convertIdToNameWithSort(id) for id in ids])]


def convertIdToEmail(id):
    if id[-2] == ':':
        student = convertIdToPerson(id[:-2])
        for index, contact in enumerate(IContactable(student).contacts):
            if id[-1] == str(index + 1):
                return contact.email or u''
        else:
            raise IndexError
    person = convertIdToPerson(id)
    return IContact(person).email or u''


def convertIdsToEmail(ids):
    return sorted([convertIdToEmail(id) for id in ids])


def getPersonsResponsible(student):
    """Return the list of persons responsible for the student.
       This includes all teachers that teach the student, the student's
       advisors, and all members of the school administation.
       Additionally, we will include the student and the student's
       parents (designated by student id + :1 or :2) in this list
       for the purpose of sending emails, but they will be filtered
       out of any application elements that are for staff only."""

    responsible = [advisor.username for advisor in student.advisors]

    term = queryUtility(IDateManager).current_term
    groups = IGroupContainer(ISchoolYear(term))
    sections = ISectionContainer(term)

    for member in groups['administrators'].members:
        if member.username not in responsible:
            responsible.append(member.username)

    for section in sections.values():
        if student in section.members:
            for teacher in section.instructors:
                if teacher.username not in responsible:
                    responsible.append(teacher.username)

    responsible.append(student.username)
    for index, contact in enumerate(IContactable(student).contacts):
        responsible.append(student.username + ':%d' % (index + 1))

    return responsible


class InterventionCrowd(crowds.Crowd):
    """Crowd of persons that can work with interventions.
       This includes teachers, school administrators and site mangers."""

    def contains(self, principal):
        if IUnauthenticatedPrincipal.providedBy(principal):
            return False
        if crowds.ManagersCrowd(self.context).contains(principal):
            return True
        if crowds.AdministratorsCrowd(self.context).contains(principal):
            return True
        if crowds.TeachersCrowd(self.context).contains(principal):
            return True
        return False


class InterventionRoot(BTreeContainer):
    """Container of InterventionSchoolYear objects."""

    implements(interfaces.IInterventionRoot)


class InterventionSchoolYear(BTreeContainer):
    """Container of InteventionStudent objects."""

    implements(interfaces.IInterventionSchoolYear)


class InterventionStudent(BTreeContainer):
    """Container of the student's intervention containers."""

    implements(interfaces.IInterventionStudent)

    def __init__(self):
        super(InterventionStudent, self).__init__()

    @property
    def student(self):
        app = ISchoolToolApplication(None)
        if self.__name__ in app['persons']:
            return app['persons'][self.__name__]
        else:
            return None


class InterventionMessages(BTreeContainer):
    """Container of Tier1 InteventionMessage objects."""

    implements(interfaces.IInterventionMessages)

    @property
    def student(self):
        try:
            return self.__parent__.student
        except:
            return None


class InterventionMessage(Persistent, Contained):
    """Intervention message about a given student."""

    implements(interfaces.IInterventionMessage, IAttributeAnnotatable)

    created = None

    def __init__(self, sender, recipients, body, status_change=False):
        self.sender = sender
        self.recipients = recipients
        self.body = body
        self.status_change = status_change
        self.created = pytz.UTC.localize(datetime.utcnow())

    @property
    def student(self):
        try:
            return self.__parent__.student
        except:
            return None


class InterventionGoals(BTreeContainer):
    """Container of InterventionGoal objects."""

    implements(interfaces.IInterventionGoals)

    @property
    def student(self):
        try:
            return self.__parent__.student
        except:
            return None


class InterventionGoal(Persistent, Contained):
    """Intervention goal for a given student."""
    implements(interfaces.IInterventionGoal, IAttributeAnnotatable)

    creator = None
    created = None

    def __init__(self, presenting_concerns, goal, strengths, indicators,
                 intervention, timeline, persons_responsible, goal_met=False,
                 follow_up_notes=u'', creator=None):
        self.presenting_concerns = presenting_concerns
        self.goal = goal
        self.strengths = strengths
        self.indicators = indicators
        self.intervention = intervention
        self.timeline = timeline
        self.persons_responsible = persons_responsible
        self.goal_met = goal_met
        self.follow_up_notes = follow_up_notes
        self.notified = False
        self.creator = creator
        self.created = pytz.UTC.localize(datetime.utcnow())

    @property
    def student(self):
        try:
            return self.__parent__.student
        except:
            return None


def setUpInterventions(app):
    app[u'schooltool.interventions'] = InterventionRoot()


class InterventionStartup(StartUpBase):
    def __call__(self):
        if u'schooltool.interventions' not in self.app:
            setUpInterventions(self.app)


class InterventionInit(InitBase):
    """Create the InterventionRoot object."""

    def __call__(self):
        setUpInterventions(self.app)


@adapter(ISchoolYear)
@implementer(interfaces.IInterventionSchoolYear)
def getSchoolYearInterventionSchoolYear(schoolyear):
    return getInterventionSchoolYear(schoolyear.__name__)


@adapter(ISchoolToolApplication)
@implementer(interfaces.IInterventionSchoolYear)
def getSchoolToolApplicationInterventionSchoolYear(app):
    return getInterventionSchoolYear()


@adapter(interfaces.IInterventionSchoolYear)
@implementer(ISchoolYear)
def getInterventionSchoolYearSchoolYear(schoolyear):
    app = ISchoolToolApplication(None)
    return ISchoolYearContainer(app)[schoolyear.__name__]


def getInterventionSchoolYear(schoolYearId=None):
    """Get InterventionSchoolYear object for the given school yearr.
       If school year not specified, use current school year."""

    app = ISchoolToolApplication(None)
    interventionRoot = app[u'schooltool.interventions']
    if not schoolYearId:
        term = queryUtility(IDateManager).current_term
        schoolyear = ISchoolYear(term)
        schoolYearId = schoolyear.__name__
    try:
        interventionSchoolYear = interventionRoot[schoolYearId]
    except KeyError:
        interventionSchoolYear = InterventionSchoolYear()
        interventionRoot[schoolYearId] = interventionSchoolYear
    return interventionSchoolYear


def getInterventionStudent(studentId, schoolYearId=None):
    """Get InterventionStudent object for the given student, school year pair.
       If school year not specified, use current school year.
       Create InterventionSchoolYear and InterventionStudent objects if
       not already present."""

    interventionSchoolYear = getInterventionSchoolYear(schoolYearId)
    try:
        interventionStudent = interventionSchoolYear[studentId]
    except KeyError:
        interventionStudent = InterventionStudent()
        interventionSchoolYear[studentId] = interventionStudent
        interventionStudent[u'messages'] = InterventionMessages()
        interventionStudent[u'goals'] = InterventionGoals()
    return interventionStudent


class SequenceNumberNameChooser(NameChooser):
    """A name chooser that returns a sequence number."""

    implements(INameChooser)

    def chooseName(self, name, obj):
        """See INameChooser."""
        numbers = [int(v.__name__) for v in self.context.values()
                   if v.__name__.isdigit()]
        if numbers:
            n = str(max(numbers) + 1)
        else:
            n = '1'
        self.checkName(n, obj)
        return n


class  InterventionStudentTraverserPlugin(NameTraverserPlugin):
    """Allows traversal to a student's intervention object.

    i.e. if you go to localhost/persons/somestudent/intervention this traverser
    will get called instead of standard traverser.  It returns the student's
    intervention object for the current school year.
    """

    traversalName = "intervention"
    def _traverse(self, request, name):
        interventionStudent = getInterventionStudent(self.context.__name__)
        return interventionStudent


class SectionMessagesProxy(object):
    """Proxy used for traversing to a section's student messages."""

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


class SectionMessagesTraverserPlugin(NameTraverserPlugin):
    """Allows traversal from a section object to a SectionMessagesProxy
    object, which in turn will be used to traverse to the messages
    container for a given student.

    i.e. if you go to localhost/sections/somesec/messages this traverser
    will get called instead of standard traverser.
    """

    traversalName = "messages"
    def _traverse(self, request, name):
        proxyMessages = SectionMessagesProxy(self.context)
        return location.LocationProxy(proxyMessages, self.context,
                                      self.traversalName)

class SectionMessagesTraverser(object):
    """Allows traversal to a student's messages container from the
    section messages proxy.

    i.e. if you go to localhost/sections/somesec/messages/someone this
    traverser will get called instead of standard traverser.  It also wraps the
    message in a location proxy so the url looks like what is above.

    The whole reason for having this traversal adaptation is to allow
    the message add view to know what section the message is for.
    """
    implements(IPublishTraverse)

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

    def publishTraverse(self, request, name):
        interventionStudent = getInterventionStudent(name)
        messages = interventionStudent['messages']
        messages = location.LocationProxy(messages, self.context, name)
        return messages

