# -*- 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
#
##############################################################################

"""Fonctions de calcul des moyennes de semestre (indicatives dans le BUT)
"""
import numpy as np
import pandas as pd

from flask import flash, g, url_for
from markupsafe import Markup

from app import db
from app.models.formations import Formation


def compute_sem_moys_apc_using_coefs(
    etud_moy_ue_df: pd.DataFrame, modimpl_coefs_df: pd.DataFrame
) -> pd.Series:
    """Calcule les moyennes générales indicatives de tous les étudiants
    = moyenne des moyennes d'UE, pondérée par la somme de leurs coefs

    etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid
    modimpl_coefs_df: DataFrame, colonnes moduleimpl_id, lignes UE (sans ue bonus)

    Result: panda Series, index etudid, valeur float (moyenne générale)
    """
    moy_gen = (etud_moy_ue_df * modimpl_coefs_df.values.sum(axis=1)).sum(
        axis=1
    ) / modimpl_coefs_df.values.sum()
    return moy_gen


def compute_sem_moys_apc_using_ects(
    etud_moy_ue_df: pd.DataFrame,
    ects_df: pd.DataFrame,
    formation_id=None,
    skip_empty_ues=False,
) -> pd.Series:
    """Calcule les moyennes générales indicatives de tous les étudiants
    = moyenne des moyennes d'UE, pondérée par leurs ECTS.

    etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid
    ects: DataFrame, col. ue_id, lignes etudid, valeur float ou None

    Si skip_empty_ues: ne compte pas les UE non notées.
    Sinon (par défaut), une UE non notée compte comme zéro.

    Result: panda Series, index etudid, valeur float (moyenne générale)
    """
    try:
        if skip_empty_ues:
            # annule les coefs des UE sans notes (NaN)
            ects = np.where(etud_moy_ue_df.isna(), 0.0, ects_df.to_numpy())
        else:
            ects = ects_df.to_numpy()
        # ects est maintenant un array nb_etuds x nb_ues

        moy_gen = (etud_moy_ue_df * ects).sum(axis=1) / ects.sum(axis=1)
    except ZeroDivisionError:
        # peut arriver si aucun module... on ignore
        moy_gen = pd.Series(np.NaN, index=etud_moy_ue_df.index)
    except TypeError:
        if None in ects:
            formation = db.session.get(Formation, formation_id)
            flash(
                Markup(
                    f"""Calcul moyenne générale impossible: ECTS des UE manquants !<br>
                (formation: <a href="{url_for("notes.ue_table", 
                scodoc_dept=g.scodoc_dept, formation_id=formation_id)}">{formation.get_titre_version()}</a>)"""
                )
            )
            moy_gen = pd.Series(np.NaN, index=etud_moy_ue_df.index)
        else:
            raise
    return moy_gen


def comp_ranks_series(notes: pd.Series) -> tuple[pd.Series, pd.Series]:
    """Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur
     numérique) en tenant compte des ex-aequos.

    Result: couple (tuple)
        Series { etudid : rang:str } où rang est une chaine decrivant le rang,
        Series { etudid : rang:int } le rang comme un nombre
    """
    if (notes is None) or (len(notes) == 0):
        return (pd.Series([], dtype=object), pd.Series([], dtype=int))
    notes = notes.sort_values(ascending=False)  # Serie, tri par ordre décroissant
    rangs_str = pd.Series("", index=notes.index, dtype=str)  # le rang est une chaîne
    # le rang numérique pour tris:
    rangs_int = pd.Series(0, index=notes.index, dtype=int)
    N = len(notes)
    nb_ex = 0  # nb d'ex-aequo consécutifs en cours
    notes_i = notes.iat
    for i, etudid in enumerate(notes.index):
        # test ex-aequo
        if i < (N - 1):
            next = notes_i[i + 1]
        else:
            next = None
        val = notes_i[i]
        if nb_ex:
            rangs_int[etudid] = i + 1 - nb_ex
            srang = "%d ex" % (i + 1 - nb_ex)
            if val == next:
                nb_ex += 1
            else:
                nb_ex = 0
        else:
            if val == next:
                rangs_int[etudid] = i + 1 - nb_ex
                srang = "%d ex" % (i + 1 - nb_ex)
                nb_ex = 1
            else:
                rangs_int[etudid] = i + 1
                srang = "%d" % (i + 1)
        rangs_str[etudid] = srang
    assert rangs_int.dtype == int
    return rangs_str, rangs_int
