#!/usr/bin/env python
# -*- coding: utf-8 -*-
#    TOMUSS: The Online Multi User Simple Spreadsheet)
#    Copyright (C) 2008-2011 Thierry EXCOFFIER, Universite Claude Bernard
#
#    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
#
#    Contact: Thierry.EXCOFFIER@bat710.univ-lyon1.fr

import os
from . import inscrits
from . import document
from . import utilities
from . import configuration

#REDEFINE
# List of LDAP OU of student to be affected to a 'referent'
def port():
    return configuration.the_portails['UFRFST'] 

#REDEFINE
# List of LDAP OU of student not to be affected to a 'referent'
def not_in():
    return ()
           
#REDEFINE
# Return True if the student need a referent teacher
def need_a_referent(login):
    """To be redefined"""
    return True

#REDEFINE
# Return True if the student must sign an interactive document
def need_a_charte(login):
    return False

#REDEFINE
# Compute the student list needing a referent
def student_list(f, pportails, not_in_list):
    f.write('<h1>' + utilities._("TITLE_referent_student_list") + '</h1>\n')
    students = {}
    print pportails
    for portail in pportails:
        f.write('<h2>' + portail + '</h2>')
        f.flush()
        for sstudent in inscrits.L_batch.member_of(portail):
            f.write(sstudent[0])
            for p in sstudent[4]:
                if p in not_in_list:
                    break
            else:
                # Not in any forbidden list
                f.write(' ')
                f.flush()
                s = Student(sstudent, portail=portail)
                if not s.licence:
                    f.write('(!L) ')
                    continue
                if len(s.ues) == 0: # Aucune IP !!!!
                    f.write('(!IP) ')
                    # XXX continue
                students[sstudent[0]] = s
                continue
            f.write('(L3) ')

    return students

def les_blocsnotes(year):
    dirname = os.path.join(configuration.db, 'Y' + str(year), 'SReferents')
    if not os.path.exists(dirname):
        return

    for ue in utilities.python_files(dirname):
        if ue == 'abjs.py':
            continue
        ue = utilities.login_to_module(ue[:-3])
        yield document.table(str(year), 'Referents', ue, ro=True)

#REDEFINE
# This function computes for the student some attributes needed to
# assign it a referent teacher.
# Attributes are :
# discipline (set), ues (list), licence, licence_first_year
def analyse_groups(student, groups):
    # Update list of the UE of the student
    for ue in groups:            
        if ue.startswith(configuration.ou_ue_starts):
            student.ues.append(ue[6:].split(' ')[0])

class Student(object):
    def __init__(self, v, portail):
        self.key = v[0]
        self.firstname = unicode(v[1], 'utf8')
        self.surname = unicode(v[2], 'utf8')
        self.mail = unicode(v[3], 'utf8')
        self.portail = portail
        self.primo_entrant = self.key[1:3] == '%2d' % (
            utilities.university_year() - 2000)

        # Get the portail of the student
        self.short_portail = ''
        for k, value in configuration.the_portails.items():
            if portail in value:
                self.short_portail = k
                break

        self.discipline = set()
        self.ues = []
        self.licence = False
        self.licence_first_year = False

        # This function compute exact values for discipline, ues, licence...
        analyse_groups(self, v[4])

    def __str__(self):
        return '%s %s %s %s %s %s' % (self.key, self.firstname, self.surname,
                                   self.mail, self.ues, self.discipline)

    def html(self):
        if self.mail is None:
            self.mail = inscrits.L_batch.mail(self.key)
        return '%s %s %s %s ues=%s disciplines=%s' % (self.key,
                                   self.firstname, self.surname,self.mail, 
                                   self.ues, list(self.discipline))

class Teacher(object):
    def __init__(self, name, discipline, line_key):
        self.name = name
        self.no_more_students = '*' in discipline
        discipline = discipline.replace('*', '')
        self.discipline = set(discipline.split(' '))
            
        self.line_key = line_key
        self.students = []
        self.nr = 0
        self.nr_weight = 0
        self.message = []

    def append(self, student):
        self.students.append(student)
        # XXX Some students, need more work for teachers
        if student.startswith('1%02d' % (utilities.university_year()%100)):
            self.nr_weight += 4
        else:
            self.nr_weight += 1
        self.nr += 1

    def __str__(self):
        return '%s %s (%f) %s' % (self.name, ' '.join(self.discipline),
                                  self.nr, self.students)


def students_of_a_teacher(tteacher):
    for line in referents_students().lines.values():
        if line[0].value == tteacher:
            s = []
            for cell in line[2:]:
                if cell.value:
                    s.append(cell.value)
            return s
    return []

def portail_of_a_teacher(tteacher):
    for line in referents_students().get_lines(tteacher):
        return line[1].value
    return ''

def referent(year, semester, login):
    """Returns the 'referent' of a student."""
    return referents_students(
        year, semester
        ).id_to_referent.get(inscrits.login_to_student_id(login))

def get(login):
    """Return the student's referent for the current semester"""
    year, semester = configuration.year_semester
    return referent(year, semester, login)

def referents_login(year, semester):
    """Returns the referent list"""
    return [t for t in referents_students(year, semester).logins() if t != '']

def referent_dict(year, semester):
    """For each student, its referent teacher"""
    d = {}
    for line in referents_students(year, semester).lines.values():
        for cell in line[2:]:
            if cell.value:
                d[cell.value] = line[0].value
    return d

def the_students(year, semester):
    """The list of student WITH a 'referent'"""
    students = []
    for line in referents_students(year, semester).lines.values():
        if line[0].value == '':
            continue
        for cell in line[2:]:
            if cell.value != '':
                students.append(cell.value)
    return students

def nr_students(year, semester):
    """Number of student with of 'referent'"""
    return len(the_students(year, semester))

#REDEFINE
# Triggered when a student is removed from a teacher
def remove_student_from_referent_hook(referent, student_id):
    return

def remove_student_from_referent(referent, student):
    table = referents_students()
    line_key, line = tuple(table.get_items(referent))[0]
    student = inscrits.login_to_student_id(student)
    for cell, column in zip(line, table.columns):
        if cell.value == student:
            table.lock()
            try:
                table.cell_change(table.pages[0], column.the_id, line_key, '')
            finally:
                table.unlock()
                remove_student_from_referent_hook(referent, student)
            break

def add_column(table):
    table.lock()
    try:
        i = len(table.columns) - 2
        table.column_change(table.pages[1],'%d'%i,str(i+1),'Text','','','',0,4)
    finally:
        table.unlock()


#REDEFINE
# Triggered when a student is added to a teacher
def add_student_to_referent_hook(referent, student_id):
    return

def add_student_to_this_line(table, line_key, line, student):
    student = inscrits.login_to_student_id(student)
    for cell, column in zip(line, table.columns)[2:]:
        if cell.value == '':
            table.lock()
            try:
                table.cell_change(table.pages[1], column.the_id, line_key,
                                  student)
            finally:
                table.unlock()
                add_student_to_referent_hook(line[0].value, student)
            break
    else:
        add_column(table)
        add_student_to_this_line(table, line_key, line, student)


def add_student_to_referent(referent, student):
    table = referents_students()
    line_key, line = tuple(table.get_items(referent))[0]
    add_student_to_this_line(table, line_key, line, student)


#REDEFINE
# This function returns the teacher to assign to a student.
# It is only needed for special case of the generic algorithm.
def search_best_teacher_local(student, sorted_teachers, f, all_teachers):
    if 'INFO_L3' in student.discipline and student.primo_entrant:
        return all_teachers['elodie.desseree']

def search_best_teacher(student, sorted_teachers, f, all_teachers):        
    """Search the teacher of a student"""

    tteacher = search_best_teacher_local(student, sorted_teachers,
                                         f, all_teachers)
    if tteacher:
        return tteacher

    # If an old referent is possible.

    k = utilities.manage_key('LOGINS',
                             os.path.join(utilities.the_login(student.key),
                                          'old_referent'))
    if k:
        k = k.strip() # \n at the end if hand edited
    if k in all_teachers \
        and all_teachers[k].discipline.intersection(student.discipline):
        return all_teachers[k]

    # The teacher is in the same discipline with the less students.
    
    for tteacher in sorted_teachers:
        if not tteacher.discipline.intersection(student.discipline) :
            continue

        # No new students for this teacher
        if tteacher.no_more_students:
            continue
        # OK for this teacher.
        return tteacher
    return None

#REDEFINE
# Returns True if the student need a referent.
# This function can heavely modify 'all_cells' and other data
# to make some adjustement.
def student_need_a_referent(student, all_cells, debug_file):
    return True

#REDEFINE
# Returns True if teacher want to keep the student even if
# the student must not have a referent teacher
def teacher_keep_student(tteacher, student_id):
    return False


def referents_students(year=None, semester=None):
    if year is None:
        year, semester = configuration.year_semester
    return document.table(year, semester, 'referents_students',
                          ro=configuration.read_only)

def update_referents(ticket, f, really_do_it = False, add_students=True):

    # Get the referent table
    table = referents_students()
    page = table.pages[0]
    # table.lock()
    # try:
    #    page = table.new_page(ticket.ticket, ticket.user_name, ticket.user_ip,
    #                          ticket.user_browser)
    # finally:
    #    table.unlock()
    f.write('''
    <link rel="stylesheet" href="../style.css" type="text/css">
    <script src="../utilities.js"></script>
    <script>display_tips = true;</script>
    ''')

    f.write('<h1>' + configuration.year_semester[1] + ' '
            + str(configuration.year_semester[0]) + '</h1>\n')
    f.write(table.filename + '\n')

    if not table.modifiable:
        f.write('<p>%s\n' % utilities._("MSG_referent_unmodifiable"))
        return

    students = student_list(f, port(), not_in())

    f.write('<h1>' + utilities._("TITLE_referent_nr_students") % len(students)
            + '</h1>\n')

    f.write('<h1>%s</h1>\n' % utilities._("TITLE_referent_remove_duplicates"))
    all_cells = {}
    all_teachers = {}

    table.lock()
    try:
        etapes_of_students = inscrits.L_batch.etapes_of_students(
            table.logins())
        for line_key, line in table.lines.items():
            if line[0].value and line[0].value not in all_teachers:
                tteacher = Teacher(line[0].value, line[1].value, line_key)
                if tteacher.discipline and not tteacher.no_more_students:
                    all_teachers[line[0].value] = tteacher
            else:
                if line[0].value:
                    f.write('%s%s<br>\n' % (
                            utilities._("MSG_referent_remove_teacher"),
                            line[0].value))
                    if really_do_it:
                        table.cell_change(page, 'a', line_key, '')
                        table.cell_change(page, 'b', line_key, '')
                tteacher = None

            for cell_key, cell in zip(table.columns, line)[2:]:
                if cell.value == '':
                    continue
                cell_key = cell_key.the_id
                if tteacher == None:
                    f.write('%s%s<br>\n' % (
                            utilities._("MSG_referent_orphan_student"),
                            cell.value))
                    if really_do_it:
                        table.cell_change(page, cell_key , line_key, '')
                    continue
                if cell.value not in all_cells:
                    all_cells[cell.value] = tteacher
                    if (not teacher_keep_student(tteacher, cell.value)
                        and cell.value not in students):
                        f.write(utilities._("MSG_referent_student_not_in_list")
                                % (cell.value, line[0].value,
                                   etapes_of_students.get(cell.value, ())
                                   ))
                        f.write(utilities._("MSG_referent_student_removed"))
                        the_student = utilities.the_login(cell.value)
                        if really_do_it:
                            remove_student_from_referent_hook(line[0].value,
                                                              cell.value)
                            table.cell_change(page, cell_key, line_key, '')
                            utilities.manage_key('LOGINS',
                                                 os.path.join(the_student,
                                                              'old_referent'),
                                                 content=line[0].value)
                        tteacher.message.append(
                            utilities.__("MSG_referent_remove_student") +
                            '%s %s' % (the_student,
                                       ' '.join(inscrits.L_slow.firstname_and_surname_and_mail(the_student))))
                        f.write('<br>\n')
                    else:
                        tteacher.append(cell.value)
                else:
                    f.write(utilities._("MSG_referent_remove_duplicate_student")
                            + '%s<br>\n' % cell.value)
                    if really_do_it:
                        remove_student_from_referent_hook(line[0].value,
                                                          cell.value)
                        table.cell_change(page, cell_key, line_key, '')

    finally:
        table.unlock()

    f.write('<h1>%s</h1>\n' % utilities._("TITLE_referent_students_in_need"))
    missing = []
    if add_students:
        for student in students.values():
            if student_need_a_referent(student, all_cells, f):
                missing.append(student.key)
                f.write(student.html().encode('utf8') + '<br>\n')

    f.write('<h1>%s</h1>\n' % utilities._("TITLE_referent_teacher_list"))
    sorted_teachers = list(all_teachers.values())
    sorted_teachers.sort(key=lambda x: x.nr_weight)
    for tteacher in sorted_teachers:
        f.write('<li>' + str(tteacher))


    f.write('<h1>%s</h1>\n' % utilities._("TITLE_referent_affectations"))
    while missing:
        sorted_teachers.sort(key=lambda x: x.nr_weight)
        s = missing[0]

        tteacher = search_best_teacher(students[s], sorted_teachers, f, all_teachers)
        if tteacher == None:
            f.write('<li> ' + utilities._("MSG_referent_no_teacher") %
                    students[s].html().encode('utf8') + '\n')
            missing.remove(s)
            continue
        ss = s
        ss += ' ' + repr(students[s].discipline)

        f.write('<li><b>' + tteacher.name + '[' + str(tteacher.nr) + ']</b> (%s): '
                % (tteacher.discipline,) + ss)
        tteacher.append(s)
        tteacher.message.append(utilities.__("MSG_referent_add_student")
                                % students[s].html())
        if really_do_it:
            add_student_to_this_line(table,
                                     tteacher.line_key,
                                     table.lines[tteacher.line_key],
                                     s)

        # Remove student from lists
        missing.remove(s)

    f.write('<h1>%s</h1>\n' % utilities._("TITLE_referent_resume"))
    my_mail = inscrits.L_slow.mail(ticket.user_name)
    for tteacher in sorted_teachers:
        if not tteacher.message:
            continue
        f.write('<li> %s<br>\n' % tteacher.name
                + '<br>\n'.join(tteacher.message).encode('utf8'))

        if really_do_it:
            mail = inscrits.L_batch.mail(tteacher.name)
            utilities.send_mail(mail,
                                utilities.__("MSG_referent_mail_subject"),
                                utilities.__("MSG_referent_mail_body") %
                                ('<br>\n'.join(tteacher.message),
                                 configuration.server_url),
                                frome=my_mail)

        
        
