#include "tp3.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TAILLE_MAT 20
#define TAILLE_NOM 26

static int nbEtu;

// Creation d'un element d'une liste de notes
T_Note *creerNote(float note, char *matiere){
    T_Note* note_temp;

    if((note_temp = (T_Note*) malloc(sizeof(T_Note))) != NULL){
      note_temp->note = note;
      if((note_temp->matiere = calloc(TAILLE_MAT, sizeof(char))) != NULL){
        strncpy(note_temp->matiere, matiere, TAILLE_MAT);
      } else {
        exit(EXIT_FAILURE);
      }
      note_temp->note_suiv = NULL;
      return note_temp;
    }
    exit(EXIT_FAILURE);
}

// Creation d'un element d'une liste d'etudiants
T_Etudiant *creerEtudiant(int idEtu, char *nom, char *prenom){
    nbEtu++;

    T_Etudiant* etu_temp;

    if ((etu_temp = malloc(sizeof(T_Etudiant))) != NULL){
      etu_temp->id = idEtu;
      if ((etu_temp->prenom = calloc(TAILLE_NOM, sizeof(char))) != NULL){
        strncpy(etu_temp->prenom, prenom, TAILLE_NOM);
      } else {
        exit(EXIT_FAILURE);
      }
      if ((etu_temp->nom = calloc(TAILLE_NOM, sizeof(char))) != NULL){
        strncpy(etu_temp->nom, nom, TAILLE_NOM);
      } else {
        exit(EXIT_FAILURE);
      }

      return etu_temp;
    }
    exit(EXIT_FAILURE);
}

T_ListeNotes ajouterNote(float note, char *matiere, T_ListeNotes listeNotes){
    T_ListeNotes listeTemp = listeNotes;
    T_ListeNotes note_temp = creerNote(note, matiere);

    while(listeTemp != NULL){
      if(!strcmp(listeTemp->matiere, matiere)){
        printf("Attention, note modifiee !\n");
        printf("Note precedente : %2.2f\n", listeTemp->note);
        listeTemp->note = note_temp->note;
        printf("Note actuelle : %2.2f\n", listeTemp->note);
        return listeNotes;
      }
      listeTemp = listeTemp->note_suiv;
    }
    note_temp->note_suiv = listeNotes;

    return note_temp;
}

// Ajout d'un note pour un etudiant dans une liste d'etudiants et renvois la liste modifiee
T_ListeEtu ajouterNoteEtu(float note, char *matiere, int idEtu, T_ListeEtu listeEtu){
    char prenom[TAILLE_NOM];
    char nom[TAILLE_NOM];
    T_ListeEtu etu_temp = NULL;
    T_ListeEtu temp = listeEtu;
    T_ListeEtu precedent = NULL;

    // Tant qu'on ne pointe pas sur NULL on continue de 
    // chercher l'étudiant
    while (temp != NULL){ // && listeEtu->etu_suiv != NULL){
        if (temp->id == idEtu){
            temp->notes = ajouterNote(note, matiere, temp->notes);
            temp->nbNotes += 1;
            temp->moyenne += (note - temp->moyenne) / temp->nbNotes;
            return listeEtu;
        }
        temp = temp->etu_suiv;
    }
    // On reprend la tete de la liste
    temp = listeEtu;

    // Si il n'est pas dans la liste on va le créer
    printf("Etudiant non trouver, veuillez donner son prenom : ");
    scanf("%s", prenom);
    printf("Son nom : ");
    scanf("%s", nom);
    etu_temp = creerEtudiant(idEtu, nom, prenom);
    etu_temp->notes = ajouterNote(note, matiere, etu_temp->notes);
    etu_temp->nbNotes = 1;
    etu_temp->moyenne += (note - etu_temp->moyenne) / etu_temp->nbNotes;

    // Cas vide
    // Si notre liste est vide on retourne
    // l'étudiant créer précédemment
    if (temp == NULL){
        return etu_temp;
    } else { 
      while ((temp != NULL) && etu_temp->id > temp->id){
          precedent = temp;
          temp = temp->etu_suiv;
      }
    }

    // Si listeEtu était non vide mais que l'id tete était
    // plus grand, alors l'étudiant créer sera la nouvelle tete
    if (precedent == NULL){
      etu_temp->etu_suiv = temp;
      temp = etu_temp;
      // On retourne temp pour avoir la nouvelle tete
      return temp;
    } else {
      precedent->etu_suiv = etu_temp;
      etu_temp->etu_suiv = temp;
    }

    return listeEtu;
}

// Suppression d'une note pour un etudiant dans une liste d'etudiants et renvois la liste modifiee
T_ListeEtu supprimerNoteEtu(char *matiere, int idEtu, T_ListeEtu listeEtu){
    T_ListeEtu temp = listeEtu;
    T_ListeEtu pEtuPrec = NULL;
    T_ListeNotes tempListeNote = NULL;
    T_ListeNotes pNotePrecedente = NULL;
    int mat = 0;

    while(temp != NULL){
      if (temp->id == idEtu){
        tempListeNote = temp->notes;
        while(tempListeNote != NULL){
          if (!strcmp(tempListeNote->matiere, matiere)){
            // Si on est à la tete des notes :
            if (pNotePrecedente == NULL){
              tempListeNote = tempListeNote->note_suiv;
              temp->nbNotes--;
              temp->moyenne += (temp->moyenne - temp->notes->note) / temp->nbNotes;
              free(temp->notes->matiere);
              free(temp->notes);
              temp->notes = tempListeNote;
              // Sert à supprimer l'étudiant qui n'a plus de notes
              if (tempListeNote == NULL){
                // Si c'est le premier etudiant
                if (pEtuPrec == NULL){
                  // On modifie directement la liste d'entrée
                  listeEtu = listeEtu->etu_suiv;
                  // On libère la mémoire
                  free(temp->nom);
                  free(temp->prenom);
                  free(temp);
                } else {
                  pEtuPrec->etu_suiv = temp->etu_suiv;
                  free(temp->nom);
                  free(temp->prenom);
                  free(temp);
                }
                nbEtu--;
              }

            } else {
              // Cas note placé au milieu
              temp->nbNotes--;
              temp->moyenne += (temp->moyenne - temp->notes->note) / temp->nbNotes;
              pNotePrecedente->note_suiv = tempListeNote->note_suiv;
              free(tempListeNote->matiere);
              free(tempListeNote);
            }
            printf("Note supprimer\n");
            return listeEtu;
          }
          pNotePrecedente = tempListeNote;
          tempListeNote = tempListeNote->note_suiv;
          mat = 1;
        }
        temp->notes = tempListeNote;
      }
      pEtuPrec = temp;
      temp = temp->etu_suiv;
    }

    if(mat){
      printf("Matiere non presente pour l'Etudiant\n");
      mat = 0;
    } else {
      printf("Etudiant non trouver\n");
    }
    return listeEtu;
}

// Affichage d'une liste d'etudiants
void afficherListeEtu(T_ListeEtu listeEtu){
  T_ListeEtu temp = listeEtu;
  while (temp != NULL){
    afficherEtu(temp);
    temp = temp->etu_suiv;
  }
}

// Affichage du classement des etudiants par ordre decroissant de la moyenne (nom, prenom, moyenne)
void afficherClassement(T_ListeEtu listeEtu){
  T_ListeEtu temp = listeEtu;

  afficherListeTrier(temp);
}

// Affichage des etudiants suivant leur reussite ou non dans une matière
void afficherSousListes(T_ListeEtu listeEtu, char *matiere){
  T_ListeEtu temp = listeEtu;
  T_ListeNotes tempNotes = temp->notes;
  T_ListeEtu *admis = NULL, *refuser = NULL, *non_present = NULL;
  int entrer = 0, i_admis = 0, i_refuser = 0, i_non_present = 0;
  int compt_admis = 0, compt_refuser = 0, compt_non_present = 0;

  if ((admis = malloc(nbEtu * sizeof(*admis))) == NULL)
    exit(EXIT_FAILURE);
  if ((refuser = malloc(nbEtu * sizeof(*refuser))) == NULL)
    exit(EXIT_FAILURE);
  if ((non_present = malloc(nbEtu * sizeof(*non_present))) == NULL)
    exit(EXIT_FAILURE);

  while(temp != NULL){
    tempNotes = temp->notes;
    while(tempNotes !=NULL){
      if (!strcmp(tempNotes->matiere, matiere)){
        if ((tempNotes->note >= 0) && (tempNotes->note < 10)){
          if (i_refuser < nbEtu){
            refuser[i_refuser++] = temp;
            compt_refuser++;
          } else {
            printf("Dépassement de mémoire, index %d hors du tableau des refuser", i_refuser);
            exit(EXIT_FAILURE);
          }
          entrer = 1;
          break;
        } else if ((tempNotes->note >= 10) && (tempNotes->note <= 20)){
          if (i_admis < nbEtu){
            admis[i_admis++] = temp;
            compt_admis++;
          } else {
            printf("Dépassement de mémoire, index %d hors du tableau des admis", i_admis);
            exit(EXIT_FAILURE);
          }
          entrer = 1;
          break;
        }
      } 
      tempNotes = tempNotes->note_suiv;
    }
    if (!entrer){
      if (i_non_present < nbEtu){
        non_present[i_non_present++] = temp;
        compt_non_present++;
      } else {
        printf("Dépassement de mémoire, index %d hors du tableau des non present", i_non_present);
        exit(EXIT_FAILURE);
      }
    }
    entrer = 0;
    temp = temp->etu_suiv;
  }

  printf("Admis :\n");
  if (admis != NULL){
    for(i_admis = 0; i_admis < compt_admis; i_admis++){
      afficherEtu(*(admis + i_admis));
    }
  }
  printf("Refuser :\n");
  if (refuser != NULL){
    for(i_refuser = 0; i_refuser < compt_refuser; i_refuser++){
      afficherEtu(*(refuser + i_refuser));
    }
  }
  printf("Non present :\n");
  if (non_present != NULL){
    for(i_non_present = 0; i_non_present < compt_non_present; i_non_present++){
      afficherEtu(*(non_present + i_non_present));
    }
  }

  free(admis);
  free(refuser);
  free(non_present);
}

// Affichage du menu de base
void afficherMenu(){
    system("clear");
    printf("1. Creer la liste d'etudiants\n");
    printf("2. Ajouter une note pour un etudiant\n");
    printf("3. Supprimer une note pour un etudiant\n");
    printf("4. Afficher la liste des etudiants\n");
    printf("5. Afficher le classement\n");
    printf("6. Ajouter les sous-listes pour une epreuve\n");
    printf("7. Quitter\n");
}

// Fonction rajouter pour afficher une liste de notes
void afficherNotes(T_ListeNotes liste){
  T_ListeNotes temp = liste;
  while(temp != NULL){
    printf("%s : %2.2f\n", temp->matiere, temp->note);
    temp = temp->note_suiv;
  }
}

// Fonction de tri
void afficherListeTrier(T_ListeEtu liste){
  T_ListeEtu listeTemp = liste, maxEtu = NULL;
  T_ListeEtu *tabTemp;
  float max;
  int i = 0, j = 0, indexMax;

  tabTemp = malloc(nbEtu * sizeof(*tabTemp));

  for (i = 0; i < nbEtu; i++, listeTemp = listeTemp->etu_suiv){
    *(tabTemp + i) = listeTemp;
  }

  while(j < nbEtu){
    for (i = j, indexMax = i, max = tabTemp[i]->moyenne, maxEtu = tabTemp[i]; i < nbEtu; i++){
      if (max < tabTemp[i]->moyenne){
        max = tabTemp[i]->moyenne;
        maxEtu = tabTemp[i];
        indexMax = i;
      }
    }
    tabTemp[indexMax] = tabTemp[j];
    tabTemp[j] = maxEtu;
    j++;
  }

  for (i = 0; i < nbEtu; i++){
    afficherEtu(*(tabTemp+i));
  }

  // On libère l'espace mémoire
  free(tabTemp);
}

void afficherEtu(T_ListeEtu listeEtu){
  T_ListeEtu temp = listeEtu;
  printf("Etudiant n%d\n", temp->id);  
  printf("Prénom : %s\n", temp->prenom);
  printf("Nom : %s\n", temp->nom);
  afficherNotes(temp->notes);
}