"""Vocabularies and sources for the student section.
"""
from zope.component import getUtility, queryUtility
from zope.catalog.interfaces import ICatalog
from zope.interface import implements, directlyProvides
from zope.schema.interfaces import ISource, IContextSourceBinder
from zope.schema.interfaces import ValidationError
from zc.sourcefactory.basic import BasicSourceFactory
from zc.sourcefactory.contextual import BasicContextualSourceFactory
from waeup.sirp.interfaces import SimpleWAeUPVocabulary, academic_sessions_vocab
from waeup.sirp.students.lgas import LGAS
from waeup.sirp.university.vocabularies import course_levels

lgas_vocab = SimpleWAeUPVocabulary(
    *sorted([(x[1],x[0]) for x in LGAS]))

def study_levels(studycourse):
    try:
        start_level = int(studycourse.certificate.start_level)
        end_level = int(studycourse.certificate.end_level)
        levels = [level for level in range(start_level,end_level+200,10)
                   if level % 100 < 30 and level < end_level + 120]
        return levels
    except AttributeError:
        return []

class StudyLevelSource(BasicContextualSourceFactory):
    """The StudyLevelSource is based on and extends the
    course_levels vocabulary defined in the university package.
    Repeating study levels are denoted by increments of 10, the
    first spillover level by the certificate's end level plus 100
    and the second spillover level by the end level plus 110.
    """
    def getValues(self, context):
        return study_levels(context)

    def getToken(self, context, value):
        return str(value)

    def getTitle(self, context, value):
        end_level = int(context.certificate.end_level)
        level,repeat = divmod(value, 100)
        level = level * 100
        repeat = repeat//10
        title = course_levels.by_value[level].title
        if level > end_level and repeat:
            title = course_levels.by_value[level-100].title
            title = "%s 2nd spillover" % title
        elif level > end_level:
            title = course_levels.by_value[level-100].title
            title = "%s spillover" % title
        elif repeat:
            title = "%s on %d. probation" % (title, repeat)
        return title

verdicts = SimpleWAeUPVocabulary(
    ('not yet','0'),
    ('Successful student','A'),
    ('Student with carryover courses','B'),
    ('Student on probation','C'),
    ('Student who were previously on probation','E'),
    ('Medical case','F'),
    ('Absent from examination','G'),
    ('Withheld results','H'),
    ('Expelled/rusticated/suspended student','I'),
    ('Temporary withdrawn from the university','J'),
    ('Unregistered student','K'),
    ('Referred student','L'),
    ('Reinstatement','M'),
    ('Student on transfer','N'),
    ('NCE-III repeater','O'),
    ('New 300 level student','X'),
    ('No previous verdict','Y'),
    ('Successful student (provisional)','Z'),
    ('First Class','A1'),
    ('Second Class Upper','A2'),
    ('Second Class Lower','A3'),
    ('Third Class','A4'),
    ('Pass','A5'),
    ('Distinction','A6'),
    ('Credit','A7'),
    ('Merit','A8'),
    )


class CertificateSource(BasicContextualSourceFactory):
    """A certificate source delivers all certificates provided
    in the portal.
    """
    def getValues(self, context):
        catalog = getUtility(ICatalog, name='certificates_catalog')
        return sorted(list(
                catalog.searchResults(
                    code=('', 'z*'))),
                    key=lambda value: value.code)

    def getToken(self, context, value):
        return value.code

    def getTitle(self, context, value):
        return "%s - %s" % (value.code, value.title[:64])


class GenderSource(BasicSourceFactory):
    """A gender source delivers basically a mapping
       ``{'m': 'Male', 'f': 'Female'}``

       Using a source, we make sure that the tokens (which are
       stored/expected for instance from CSV files) are something one
       can expect and not cryptic IntIDs.
    """
    def getValues(self):
        return ['m', 'f']

    def getToken(self, value):
        return value[0].lower()

    def getTitle(self, value):
        if value == 'm':
            return 'Male'
        if value == 'f':
            return 'Female'

class RegNumNotInSource(ValidationError):
    """Registration number exists already
    """
    # The docstring of ValidationErrors is used as error description
    # by zope.formlib.
    pass

class MatNumNotInSource(ValidationError):
    """Matriculation number exists already
    """
    # The docstring of ValidationErrors is used as error description
    # by zope.formlib.
    pass

class RegNumberSource(object):
    implements(ISource)
    cat_name = 'students_catalog'
    field_name = 'reg_number'
    validation_error = RegNumNotInSource
    def __init__(self, context):
        self.context = context
        return

    def __contains__(self, value):
        cat = queryUtility(ICatalog, self.cat_name)
        if cat is None:
            return True
        kw = {self.field_name: (value, value)}
        results = cat.searchResults(**kw)
        for entry in results:
            if entry.student_id != self.context.student_id:
                # XXX: sources should simply return False.
                #      But then we get some stupid error message in forms
                #      when validation fails.
                raise self.validation_error(value)
                #return False
        return True

def contextual_reg_num_source(context):
    source = RegNumberSource(context)
    return source
directlyProvides(contextual_reg_num_source, IContextSourceBinder)

class MatNumberSource(RegNumberSource):
    field_name = 'matric_number'
    validation_error = MatNumNotInSource

def contextual_mat_num_source(context):
    source = MatNumberSource(context)
    return source
directlyProvides(contextual_mat_num_source, IContextSourceBinder)
