# -*- mode: python -*-
# -*- coding: utf-8 -*-

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2023 Emmanuel Viennet.  All rights reserved.
#
# 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
#
#   Emmanuel Viennet      emmanuel.viennet@viennet.net
#
##############################################################################

"""Edition des lettres individuelles de jury
"""
# code initialement dans sco_pvpdf.py

import io
import re

from PIL import Image as PILImage
from PIL import UnidentifiedImageError

import reportlab
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_LEFT
from reportlab.platypus import PageBreak, Table, Image
from reportlab.platypus.doctemplate import BaseDocTemplate
from reportlab.lib import styles

from app.models import FormSemestre, Identite

import app.scodoc.sco_utils as scu
from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_pv_dict
from app.scodoc import sco_pdf
from app.scodoc import sco_preferences
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_cursus_dut import SituationEtudCursus
from app.scodoc.sco_pv_templates import CourrierIndividuelTemplate, jury_titres
import sco_version


def pdf_lettres_individuelles(
    formsemestre_id,
    etudids=None,
    date_jury="",
    date_commission="",
    signature=None,
):
    """Document PDF avec les lettres d'avis pour les etudiants mentionnés
    (tous ceux du semestre, ou la liste indiquée par etudids)
    Renvoie pdf data ou chaine vide si aucun etudiant avec décision de jury.
    """
    dpv = sco_pv_dict.dict_pvjury(formsemestre_id, etudids=etudids, with_prev=True)
    if not dpv:
        return ""
    #
    formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
    prefs = sco_preferences.SemPreferences(formsemestre_id)
    params = {
        "date_jury": date_jury,
        "date_commission": date_commission,
        "titre_formation": dpv["formation"]["titre_officiel"],
        "htab1": "8cm",  # lignes à droite (entete, signature)
        "htab2": "1cm",
    }
    # copie preferences
    for name in sco_preferences.get_base_preferences().prefs_name:
        params[name] = sco_preferences.get_preference(name, formsemestre_id)

    bookmarks = {}
    objects = []  # list of PLATYPUS objects
    npages = 0
    for decision in dpv["decisions"]:
        if (
            decision["decision_sem"]
            or decision.get("decision_annee")
            or decision.get("decision_rcue")
            or decision.get("decisions_ue")
        ):  # decision prise
            etud = Identite.get_etud(decision["identite"]["etudid"])
            params["nomEtud"] = etud.nomprenom  # backward compat
            bookmarks[npages + 1] = scu.suppress_accents(etud.nomprenom)
            try:
                objects += pdf_lettre_individuelle(
                    dpv["formsemestre"], decision, etud, params, signature
                )
            except UnidentifiedImageError as exc:
                raise ScoValueError(
                    "Fichier image (signature ou logo ?) invalide !"
                ) from exc
            objects.append(PageBreak())
            npages += 1
    if npages == 0:
        return ""
    # Paramètres de mise en page
    margins = (
        prefs["left_margin"],
        prefs["top_margin"],
        prefs["right_margin"],
        prefs["bottom_margin"],
    )

    # ----- Build PDF
    report = io.BytesIO()  # in-memory document, no disk file
    document = BaseDocTemplate(report)
    document.addPageTemplates(
        CourrierIndividuelTemplate(
            document,
            author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)",
            title=f"Lettres décision {formsemestre.titre_annee()}",
            subject="Décision jury",
            margins=margins,
            pagesbookmarks=bookmarks,
            preferences=prefs,
        )
    )

    document.build(objects)
    data = report.getvalue()
    return data


def _simulate_br(paragraph_txt: str, para="<para>") -> str:
    """Reportlab bug turnaround (could be removed in a future version).
    p is a string with Reportlab intra-paragraph XML tags.
    Replaces <br> (currently ignored by Reportlab) by </para><para>
    Also replaces <br> by <br/>
    """
    return ("</para>" + para).join(
        re.split(r"<.*?br.*?/>", paragraph_txt.replace("<br>", "<br/>"))
    )


def _make_signature_image(signature, leftindent, formsemestre_id) -> Table:
    "crée un paragraphe avec l'image signature"
    # cree une image PIL pour avoir la taille (W,H)

    f = io.BytesIO(signature)
    img = PILImage.open(f)
    width, height = img.size
    pdfheight = (
        1.0
        * sco_preferences.get_preference("pv_sig_image_height", formsemestre_id)
        * mm
    )
    f.seek(0, 0)

    style = styles.ParagraphStyle({})
    style.leading = 1.0 * sco_preferences.get_preference(
        "SCOLAR_FONT_SIZE", formsemestre_id
    )  # vertical space
    style.leftIndent = leftindent
    return Table(
        [("", Image(f, width=width * pdfheight / float(height), height=pdfheight))],
        colWidths=(9 * cm, 7 * cm),
    )


def pdf_lettre_individuelle(sem, decision, etud: Identite, params, signature=None):
    """
    Renvoie une liste d'objets PLATYPUS pour intégration
    dans un autre document.
    """
    #
    formsemestre_id = sem["formsemestre_id"]
    formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
    situation_etud: SituationEtudCursus = decision["Se"]
    titre_jury, titre_jury_court = jury_titres(
        formsemestre,
        situation_etud.parcours_validated() or not situation_etud.semestre_non_terminal,
    )
    objects = []
    style = reportlab.lib.styles.ParagraphStyle({})
    style.fontSize = 14
    style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id)
    style.leading = 18
    style.alignment = TA_LEFT

    params["semestre_id"] = formsemestre.semestre_id
    params["decision_sem_descr"] = decision["decision_sem_descr"]
    params["type_jury"] = titre_jury  # type de jury (passage ou delivrance)
    params["type_jury_abbrv"] = titre_jury_court  # idem, abbrégé
    params["decisions_ue_descr"] = decision["decisions_ue_descr"]
    if decision["decisions_ue_nb"] > 1:
        params["decisions_ue_descr_plural"] = "s"
    else:
        params["decisions_ue_descr_plural"] = ""

    params["INSTITUTION_CITY"] = (
        sco_preferences.get_preference("INSTITUTION_CITY", formsemestre_id) or ""
    )

    if decision["prev_decision_sem"]:
        params["prev_semestre_id"] = decision["prev"]["semestre_id"]

    params["prev_decision_sem_txt"] = ""
    params["decision_orig"] = ""

    params.update(decision["identite"])
    # fix domicile
    if params.get("domicile"):
        params["domicile"] = params["domicile"].replace("\\n", "<br/>")

    # UE capitalisées:
    if decision["decisions_ue"] and decision["decisions_ue_descr"]:
        params["decision_ue_txt"] = (
            """<b>Unité%(decisions_ue_descr_plural)s d'Enseignement %(decision_orig)s capitalisée%(decisions_ue_descr_plural)s : %(decisions_ue_descr)s</b>"""
            % params
        )
    else:
        params["decision_ue_txt"] = ""
    # Mention
    params["mention"] = decision["mention"]
    # Informations sur compensations
    if decision["observation"]:
        params["observation_txt"] = (
            """<b>Observation :</b> %(observation)s.""" % decision
        )
    else:
        params["observation_txt"] = ""
    # Autorisations de passage
    if decision["autorisations"] and not situation_etud.parcours_validated():
        if len(decision["autorisations"]) > 1:
            titre_jury_court = "s"
        else:
            titre_jury_court = ""
        params[
            "autorisations_txt"
        ] = """Vous êtes autorisé%s à continuer dans le%s semestre%s : <b>%s</b>""" % (
            etud.e,
            titre_jury_court,
            titre_jury_court,
            decision["autorisations_descr"],
        )
    else:
        params["autorisations_txt"] = ""

    if decision["decision_sem"] and situation_etud.parcours_validated():
        params["diplome_txt"] = (
            """Vous avez donc obtenu le diplôme : <b>%(titre_formation)s</b>""" % params
        )
    else:
        params["diplome_txt"] = ""

    # Les fonctions ci-dessous ajoutent ou modifient des champs:
    if formsemestre.formation.is_apc():
        # ajout champs spécifiques PV BUT
        add_apc_infos(formsemestre, params, decision)
    else:
        # ajout champs spécifiques PV DUT
        add_classic_infos(formsemestre, params, decision)

    # Corps de la lettre:
    objects += sco_bulletins_pdf.process_field(
        sco_preferences.get_preference("PV_LETTER_TEMPLATE", sem["formsemestre_id"]),
        params,
        style,
        suppress_empty_pars=True,
    )

    # Signature:
    # nota: si semestre terminal, signature par directeur IUT, sinon, signature par
    # chef de département.
    if situation_etud.semestre_non_terminal:
        sig = (
            sco_preferences.get_preference(
                "PV_LETTER_PASSAGE_SIGNATURE", formsemestre_id
            )
            or ""
        ) % params
        sig = _simulate_br(sig, '<para leftindent="%(htab1)s">')
        objects += sco_pdf.make_paras(
            (
                """<para leftindent="%(htab1)s" spaceBefore="25mm">"""
                + sig
                + """</para>"""
            )
            % params,
            style,
        )
    else:
        sig = (
            sco_preferences.get_preference(
                "PV_LETTER_DIPLOMA_SIGNATURE", formsemestre_id
            )
            or ""
        ) % params
        sig = _simulate_br(sig, '<para leftindent="%(htab1)s">')
        objects += sco_pdf.make_paras(
            (
                """<para leftindent="%(htab1)s" spaceBefore="25mm">"""
                + sig
                + """</para>"""
            )
            % params,
            style,
        )

    if signature:
        try:
            objects.append(
                _make_signature_image(signature, params["htab1"], formsemestre_id)
            )
        except UnidentifiedImageError as exc:
            raise ScoValueError("Image signature invalide !") from exc

    return objects


def add_classic_infos(formsemestre: FormSemestre, params: dict, decision: dict):
    """Ajoute les champs pour les formations classiques, donc avec codes semestres"""
    if decision["prev_decision_sem"]:
        params["prev_code_descr"] = decision["prev_code_descr"]
        params[
            "prev_decision_sem_txt"
        ] = f"""<b>Décision du semestre antérieur S{params['prev_semestre_id']} :</b> {
            params['prev_code_descr']}"""
    # Décision semestre courant:
    if formsemestre.semestre_id >= 0:
        params["decision_orig"] = f"du semestre S{formsemestre.semestre_id}"
    else:
        params["decision_orig"] = ""


def add_apc_infos(formsemestre: FormSemestre, params: dict, decision: dict):
    """Ajoute les champs pour les formations APC (BUT), donc avec codes RCUE et année"""
    annee_but = (formsemestre.semestre_id + 1) // 2
    params["decision_orig"] = f"année BUT{annee_but}"
    if decision is None:
        params["decision_sem_descr"] = ""
        params["decision_ue_txt"] = ""
    else:
        decision_annee = decision.get("decision_annee") or {}
        params["decision_sem_descr"] = decision_annee.get("code") or ""
        params[
            "decision_ue_txt"
        ] = f"""{params["decision_ue_txt"]}<br/>
            <b>Niveaux de compétences:</b><br/> {decision.get("descr_decisions_rcue") or ""}
        """