package fr.iutfbleau.projetIHM2022FI2.MP;
import fr.iutfbleau.projetIHM2022FI2.API.*;
import java.sql.Connection;
import org.mariadb.jdbc.*;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.util.*;
/**
 * Usine abstraite gérant l'ensemble des groupes.
 * 
 */

public class AbstractGroupeFactoryPersistant implements AbstractGroupeFactory {

    // la racine (promotion)
    private Groupe promo;
    
    //la fentre pour les fenetre modale
    private JFrame fenetre;

    // On utilise une table de hachage pour retrouver facilement un groupe (à partir de son id).
    // Si il y a beaucoup de groupes c'est plus rapide que de parcourir toute une liste.
    private HashMap<Integer,Groupe> brain;

    /**
     * Le constructeur fabrique le groupe promotion déja plein (utilisé en Modèle persistant de donné).
		 * @param fenetre	fenetre principale
		 *
     */
    public AbstractGroupeFactoryPersistant(JFrame fenetre){
        this.fenetre=fenetre;
        this.brain=new HashMap<Integer,Groupe>();
        this.init();
    }

    /**
     * Le constructeur fabrique le groupe promotion vide.
     * Il faut ensuite y ajouter les étudiants.
		 * @param fenetre	fenetre principale
		 * @param name nom de la promotion
		 * @param min le nombre minimum d'étudiants dans la promotion
		 * @param max le nombre maximum d'étudiants dans la promotion
     */
    public AbstractGroupeFactoryPersistant(String name, int min, int max, JFrame fenetre){
        Objects.requireNonNull(name,"On ne peut pas créer une promotion  dont le nom est null");
        this.promo=new GroupeNP(name,min,max);
        this.fenetre=fenetre;
        this.brain=new HashMap<Integer,Groupe>();
        this.brain.put(Integer.valueOf(this.promo.getId()),this.promo);
        this.saveGroupe(promo);
    }

    
    /**
     * Test plutôt optimiste. Si la clé est identique alors on fait comme si c'était le bon groupe.
     * @param g le groupe à tester
     * @return true si le groupe est connu
     */
    public Boolean knows(Groupe g){
        return this.brain.containsKey(Integer.valueOf(g.getId()));
    }

		/**	
		 * pemet de changer le nom d'un groupe dans la base de données
		 * 
		 * @param g le groupe dont on veut changer le nom
		 * @param name le nouveau nom
		 *
		 * @return true si le changement a été effectué, false s'il y a eu une erreur
		 *
		 *
		 */
    public boolean changeNameGroupe(Groupe g, String name){
        if(!this.knows(g)){
            throw new IllegalArgumentException("Groupe inconu ");
        }
        Connection cnx=this.cnx();
        try{
            PreparedStatement pst=cnx.prepareStatement(
                "UPDATE `Groupe` SET `nom` = ? WHERE `Groupe`.`id` = ? "
            );
            pst.setString(1, name);
            pst.setInt(2, g.getId());
            pst.executeUpdate();
            pst.close();
        }catch(SQLException e){
            System.out.println(e.toString());
            if(this.erreurSQL()){
                this.changeNameGroupe(g, name);
            }else{
                return false;
            }
        }
        this.close(cnx);
        g.setName(name);
        return true;
    }



    /**
     * permet de récupérer le Groupe qui contient les étudiants de toute la promotion
     * @return la promo.
     */
    public Groupe getPromotion(){
        return this.promo;
    }

    /**
     * permet de supprimer un groupe connu de l'usine abstraite qui ne contient pas de groupes.
     * Pour détruire un groupe connu qui en contient d'autres il faut le faire récursivement.
     *
		 * @param g le groupe à supprimer
		 * 
     * @throws java.lang.NullPointerException si un argument est null
     * @throws java.lang.IllegalStateException si le groupe contient des groupes
     * @throws java.lang.IllegalArgumentException si le groupe n'est pas connu de l'usine abstraite ou bien si le groupe est celui de toute la promotion (renvoyé par getPromotion)
     */
    public void deleteGroupe(Groupe g){
        Objects.requireNonNull(g,"On ne peut pas enlever un groupe null car null n'est pas un groupe autorisé");
        if (!this.knows(g)){
            throw new IllegalArgumentException("Impossible d'enlever un groupe inconnu");
        }
        g.getPointPoint().removeSousGroupe(g);
        this.brain.remove(Integer.valueOf(g.getId()));
        this.suprGroupe(g);
    }

    /**
     * permet d'ajouter un groupe vide de type FREE comme sous-groupe d'un groupe donné.
     * @param pere le groupe père du groupe à créer 
     * @param name le nom du groupe à créer
     * @param min,max bornes indicatives sur la taille du groupe à créer
     *
     * @throws java.lang.NullPointerException si un argument est null
     * @throws java.lang.IllegalArgumentException si le groupe pere est de type PARTITION
     *                                            ou si il n'y a pas 0 < min <= max 
     */
    public void createGroupe(Groupe pere, String name, int min, int max){
        Objects.requireNonNull(pere,"Le groupe pere ne peut pas être null");
        Objects.requireNonNull(name,"Le nouveau groupe ne peut pas avoir null comme nom");

        if (!this.knows(pere)){
           throw new IllegalArgumentException("Interdit d'ajouter un fils à un groupe inconnu");
        }
        if (pere.getType().equals(TypeGroupe.PARTITION)){
            throw new IllegalArgumentException("Impossible d'ajouter un groupe à une parition. Il faut utiliser createPartition pour créer une partition");
        }
        if ( min <= 0 || max < min){
            throw new IllegalArgumentException("Il faut que 0 < min <= max");
        }

        Groupe g = new GroupeNP(pere,name,min,max);
        pere.addSousGroupe(g);
        this.brain.put(Integer.valueOf(g.getId()),g);
        this.saveGroupe(g);
    }

    /**
     * permet de créer une partition automatiquement sous un groupe donné.
     *
     * @param pere le groupe père du groupe à partitionner 
     * @param name le nom des groupe à créer (on ajoute à la suite un numéro de 1 à n  pour distinguer chaque groupe formant la partition)
     * @param n le nombre de partitions
     * @throws java.lang.NullPointerException si un argument est null
     * @throws java.lang.IllegalArgumentException si le groupe pere est de type PARTITION 
     *                                            ou n négatif ou nul
     *
     * NB. doit créer une "copie" de pere 
     *     sous pere de type Partition et ajouter sous ce groupe, n groupes de type "FREE". 
     *     les valeurs min et max de ces n groupes sont 
     *       min = 0 et 
     *       max = partie entière de N/n plus 1, où N est le nombre max du groupe pere.   
     */
    public void createPartition(Groupe pere, String name, int n){
        Objects.requireNonNull(pere,"Le groupe pere ne peut pas être null");
        Objects.requireNonNull(name,"Le nouveau groupe ne peut pas avoir null comme nom");
        if (!this.knows(pere)){
            throw new IllegalArgumentException("Impossible de partitionner ce groupe inconnu");
        }
        if (pere.getType().equals(TypeGroupe.PARTITION)){
            throw new IllegalArgumentException("Impossible de créer une partition à ce niveau. Il faut soit repartitionner le groupe au dessus, soit partitionner une partition en dessous.");
        }
        if ( n <= 0){
            throw new IllegalArgumentException("Le nombre de partitions doit être strictement positif");
        }
        //Création de la racine de la partition.
        Groupe copiePereRacinePartition = new GroupeNP(pere);
        pere.addSousGroupe(copiePereRacinePartition);
        this.brain.put(Integer.valueOf(copiePereRacinePartition.getId()),copiePereRacinePartition);
        // création des sous-groupes
        int min = 0;
        int max = ((int) Math.floor(pere.getSize()/n))+1;
        List<Groupe> groupes = new ArrayList<Groupe>(n);
        for(int i = 0; i<n; i++){
            Groupe g = new GroupeNP(copiePereRacinePartition,name+"_"+i,min,max);
            groupes.add(i,g);// ajout dans le tableau des groupes
            copiePereRacinePartition.addSousGroupe(g);
            this.brain.put(Integer.valueOf(g.getId()),g);
        }
        // Partage des étudiants (on ne prête pas attention aux min et max)
        int i=0;
        for (Etudiant s: pere.getEtudiants()){
            copiePereRacinePartition.addEtudiant(s);
            groupes.get(i).addEtudiant(s);
            i = (i+1) %n;
        }
        this.saveGroupe(copiePereRacinePartition);
    }

    /**
     * permet d'ajouter un étudiant à un groupe.
     *
     * @param g le groupe dans lequel il faut ajouter l'étudiant
     * @param e l'étudiant à ajouter
     *
     * @throws java.lang.NullPointerException si un argument est null
     * @throws java.lang.IllegalArgumentException la factory ne connaît pas g
     * @throws java.lang.IllegalStateException le père de g ne contient pas e
     */
    public void addToGroupe(Groupe g, Etudiant e){
        Objects.requireNonNull(g,"Le groupe ne peut pas être null");
        Objects.requireNonNull(e,"L'étudiant ne peut pas être null");
        if (!this.knows(g)){
            throw new IllegalArgumentException("Impossible d'ajouter l'étudiant car le est groupe inconnu");
        }
        g.addEtudiant(e);
        this.saveEtu(e, g);
    }

    /**
     * permet d'enlever un étudiant d'un groupe.
     *
     * @param g le groupe dans lequel il faut enlever l'étudiant
     * @param e l'étudiant à enlever
     *
     * @throws java.lang.NullPointerException si un argument est null
     * @throws java.lang.IllegalStateException g ne contient pas e
     * @throws java.lang.IllegalArgumentException la factory ne connaît pas g
     */
    public void dropFromGroupe(Groupe g, Etudiant e){
        Objects.requireNonNull(g,"Le groupe ne peut pas être null");
        Objects.requireNonNull(e,"L'étudiant ne peut pas être null");
        if (!this.knows(g)){
            throw new IllegalArgumentException("Impossible de suprimer l'étudiant car le est groupe inconnu");
        }
        g.removeEtudiant(e);
        this.deleteEtu(e, g);
    }

     /**
     * permet de retrouver un étudiant à partir d'un String.
     *
     * NB. dans une version simple il doit s'agir du nom exact.
     * dans une version un peu plus complexe, il s'agit des premières lettres du nom
     * dans une version avancée, on peut autoriser une expression régulière plus ou moins complexe qui est générée si la première recherche n'a pas renvoyé de candidat.
     *
     * @param String nomEtu le nom approximmatif de l'étudiant
     * @return Set<Etudiant> l'ensemble des étudiants connus de la factory ayant un nom "proche" de ce string au sens de la remarque ci-dessus.
     *
     * @throws java.lang.NullPointerException si le String est null.
     */
    public Set<Etudiant> getEtudiants(String nomEtu){
        Set<Etudiant> s=new LinkedHashSet<>();
        //on retourne tous les etudiants
        if(nomEtu==null){
            if(this.getPromotion()!=null){
                for(Etudiant et:this.getPromotion().getEtudiants()){
                    s.add(et);
                }
            }
            //ceux qui sont connu mais pas dans la promo
            Connection cnx=this.cnx();
            try{
                PreparedStatement pst=cnx.prepareStatement("SELECT * FROM Etudiant where Etudiant.id NOT IN (SELECT CONTIENT.idEt FROM CONTIENT) Group by Etudiant.id;");
                ResultSet rs=pst.executeQuery();
                while(rs.next()){
                    s.add(new EtudiantNP(rs.getString(2), rs.getString(3), rs.getInt(1)));
                }
            }catch(SQLException e){
                System.out.println(e.toString());
                if(this.erreurCO())
                    return this.getEtudiants(nomEtu);
                return null;
            }
            this.close(cnx);
        }else{
            for(Etudiant e:this.getPromotion().getEtudiants()){
                if((e.getNom()).contains(nomEtu)){
                    s.add(e);
                }
            }
        }
        return s;
    }

    /**
     * permet de retrouver les groupes d'un étudiant.
     *
     * @param Etu un étudiant
     * @return Etudiant l'étudiant connu de la factory ayant cet identifiant
     *
     * @throws java.lang.NullPointerException si le String est null.
     */
    public Set<Groupe> getGroupesOfEtudiant(Etudiant etu){
        if(etu==null)
            throw new NullPointerException();
        Set<Groupe> ret=new LinkedHashSet<Groupe>();
        Connection cnx=this.cnx();
        try{
            PreparedStatement pst=cnx.prepareStatement("SELECT `idGroupe` FROM CONTIENT where idEt=? Group by `idGroupe`;");
            pst.setInt(1, etu.getId());
            ResultSet rs=pst.executeQuery();
            while(rs.next()){
                ret.add(this.brain.get(rs.getInt(1)));
            }
            rs.close();
            pst.close();
        }catch(SQLException e){

        }
        this.close(cnx);
        return ret;
    }

    // **********************
    // FONCTION POUR SIMPLIFIER LES Modification BD
    // *********************** 

		
		/**
		 * permet de se connecter à la base de donnée
		 *
		 * @return la connexion à la base de donnée
		 */
    private Connection cnx(){
    //On se Connecte a la BD
    try{
        Class.forName("org.mariadb.jdbc.Driver");
        Connection cnx = DriverManager.getConnection(
        "jdbc:mariadb://dwarves.iut-fbleau.fr/chaignea",
        "chaignea", "Chaigneauphpmyadmin");
        return cnx;
    }catch(Exception e){
        System.out.println(e.toString());
        if(this.erreurCO()==true){
            return this.cnx();
        }
    }
    return null;
    }
		/**
		 * permet de détécter une erreur de connexion à la base de donnée
		 * 
		 * @return true si une erreur est détéctée, false sinon
		 *
		 * 
		 */
    private boolean erreurCO(){
        if(JOptionPane.showConfirmDialog(this.fenetre, "erreur connection a la BD reassayer?", "erreur connection", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){
            return true;
        }else{
            this.fenetre.dispose();
            return false;
        }
    }
		/**
		 * permet de détécter une erreur SQL
		 * 
		 * @return true si une erreur est détéctée, false sinon
		 *
		 * 
		 */
    private boolean erreurSQL(){
        if(JOptionPane.showConfirmDialog(this.fenetre, "erreur lors de la modification, reasssayer?", "erreur SQL", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){
            return true;
        }else{
            return false;
        }
    }
		/**
		 * permet de supprimer un étudiant de la base de donnée
		 * @param et l'étudiant à supprimer
		 * @param g le groupe dans lequel il est
		 */
    private void deleteEtu(Etudiant et, Groupe g){
    Connection cnx=this.cnx();
    try{
        PreparedStatement pst=cnx.prepareStatement(
            "DELETE FROM CONTIENT WHERE idGroupe=? AND idEt=?; "
        );
        pst.setInt(2, et.getId());
        this.deleteEtu(pst, cnx, g.getId());
        pst.close();
    }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurSQL()){
            this.deleteEtu(et, brain.get(g.getId()));
        }
    }
    this.close(cnx);
    }
		/**
		 * permet de supprimer un étudiant de la base de donnée
		 * @param pst la requete préparée
		 * @param cnx la connexion à la base de donnée
		 * @param id le groupe parent
		 */
    private void deleteEtu(PreparedStatement pst, Connection cnx, int id){
    try{
        pst.setInt(1, id);
        pst.executeUpdate();

        PreparedStatement sous=cnx.prepareStatement(
            "SELECT * FROM Groupe WHERE `id-parent`=? AND `id-parent`!=id; "
        );
        sous.setInt(1, id);
        ResultSet rs=sous.executeQuery();
        while(rs.next()){
            this.deleteEtu(pst, cnx, rs.getInt(1));
        }
    }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurSQL()){
            this.deleteEtu(pst, cnx, id);
        }
    }
    }
		/**
		 * permet de fermer la connexion à la base de donnée
		 * 
		 * @param clos outil de fermture de la connexion
		 * 
		 */
    private void close(AutoCloseable clos){
        try{
            clos.close();
        }catch(Exception e){
            System.out.println(e.toString());
            if(this.erreurCO()==true)
                this.close(clos);
        }
    }

/**
 * permet d'ajouter un groupe de la base de donnée
 * @param etudiant l'étudiant à ajouter
 * @param g le groupe de l'étudiant
 * @return true s'il n'y a pas eu d'erreur, false sinon
 */ 
private boolean saveEtu(Etudiant etudiant, Groupe g){
    Connection cnx = this.cnx();
    try{
        PreparedStatement pst=cnx.prepareStatement(
            "Select id from Etudiant where id=?; ");
        pst.setString(1, String.valueOf(etudiant.getId()));
        ResultSet rs=pst.executeQuery();
        if(rs.next()){
            //L'etudiant est déja connu de la BD
            pst.close();
            pst=cnx.prepareStatement(
                "INSERT INTO `CONTIENT` (`idGroupe`, `idEt`) VALUES (?, ?);");
            pst.setInt(2, etudiant.getId());
            pst.setInt(1, g.getId());
            pst.executeUpdate();
        }else{
            pst.close();
            pst=cnx.prepareStatement(
                "INSERT INTO `Etudiant` (`id`, `nom`, `prenom`) VALUES (?, ?, ?) ;");
            pst.setInt(1, etudiant.getId());
            pst.setString(2, etudiant.getNom());
            pst.setString(3, etudiant.getPrenom());
            pst.executeUpdate();
        }
        rs.close();
        pst.close();
    }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurSQL()){
            this.saveEtu(etudiant, g);
        }else{
            return false;
        }
    }
    this.close(cnx);
    return true;
    }
		/**
		 * permet d'ajouter un groupe dans la base de donnée
		 * @param g le groupe à ajouter
		 * @return true s'il n'y a pas eu d'erreur, false sinon
		 */
    private boolean saveGroupe(Groupe g){
    Connection cnx=this.cnx();
    try{
        PreparedStatement pst=cnx.prepareStatement(
            "INSERT INTO `Groupe` (`id`, `nom`, `min`, `max`, `Type`, `id-parent`) VALUES (?, ?, ?, ?, ?, ?);"
        );
        pst.setInt(1, g.getId());
        pst.setString(2, g.getName());
        pst.setInt(3, g.getMin());
        pst.setInt(4, g.getMax());
        pst.setString(5, g.getType().name());
        pst.setInt(6, g.getPointPoint().getId());
        pst.executeUpdate();
        pst.close();
        for(Etudiant e: g.getEtudiants()){
            this.saveEtu(e, g);
        }
        for(Groupe sous:g.getSousGroupes()){
            this.saveGroupe(sous);
        }
    }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurSQL()){
            this.saveGroupe(g);
        }else{
            return false;
        }
    }
    this.close(cnx);
    return true;
    }
		/**
		 * permet de supprimer un groupe de la base de donnée
		 * @param g le groupe à supprimer
		 * @return true s'il n'y a pas eu d'erreur, false sinon
		 */
    private boolean suprGroupe(Groupe g){
        Connection cnx=this.cnx();
        try{
            PreparedStatement pst=cnx.prepareStatement("Delete FROM Groupe where id=?;");
            pst.setInt(1, g.getId());
            pst.executeUpdate();
            pst.close();
            pst=cnx.prepareStatement("Delete FROM CONTIENT where idGroupe=?;");
            pst.setInt(1, g.getId());
            pst.executeUpdate();
            pst.close();
        }catch(SQLException e){
            System.out.println(e.toString());
            if(this.erreurSQL()){
                this.suprGroupe(g);
            }else{
                return false;
            }
        }
        this.close(cnx);
        return true;
    }



    /*****************************
     * FONCTION POUR INITIALISER LA FACTORY AVEC LA BD
     * ********************************
     */
		/**		
		 * permet de charger les groupes de la base de donnée
		 */
    private void init(){
        Connection cnx=this.cnx();
      //on récupère le Groupe de la BD n'ayant pas de Parent (La promo donc)
      try{
        PreparedStatement pst = cnx.prepareStatement(
      "SELECT `id`, `nom`, `min`, `max`, `value`, `id-parent` FROM `Groupe` join `TYPE` on Groupe.Type=TYPE.name ORDER BY Groupe.id ASC;");
        ResultSet rs = pst.executeQuery();
        rs=pst.executeQuery();
          //Si il existe bien une promotion
          if(rs.next()){
            //On créer le groupe de promo
            this.promo=new GroupeNP(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getInt(4), TypeGroupe.getType(rs.getString(5)), null);
            //on ajoute au cerveau
            this.brain.put(this.promo.getId(), this.promo);
            //On lui ajoute tout ses sous-groupe
            this.addSousGroupe(this.promo, cnx);
            //On ajoute les étudiants:
            for(Groupe sous: this.brain.values()){
                this.addBDEtudiant(sous, cnx);
            }
          }else{
            //Si aucune ligne et donc pas de promo:
            this.promo=null;
          }
        rs.close();
        pst.close();
      }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurCO())
            this.init();
      }
      this.close(cnx);
    }

    /**
     * Fonction recursive permettant de récuperrer les sous groupe a partir de la BD
     * @param g le groupe
     * @param cnx la connection a la BD (evite de la surcharger)
     */
    private void addSousGroupe(Groupe g, Connection cnx){
        try{
          //On récupere les Groupe qui ont le parent :g
          PreparedStatement pst= cnx.prepareStatement(
            "SELECT `id`, `nom`, `min`, `max`, `value`, `id-parent` FROM `Groupe` join `TYPE` on Groupe.Type=TYPE.name where Groupe.`id-parent`=? and Groupe.id!=Groupe.`id-parent`;");
        pst.setString(1, String.valueOf(g.getId()));
        ResultSet rs=pst.executeQuery();
        rs=pst.executeQuery();
        //autrement si le groupe as des sous groupe
        while(rs.next()){
            //on les ajoute
            Groupe nouveau=new GroupeNP(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getInt(4), TypeGroupe.getType(rs.getString(5)), g);
            //Si one le connait pas
            if(this.brain.get(nouveau.getId())==null){
                this.brain.put(nouveau.getId(), nouveau);
            }
            g.addSousGroupe(nouveau);
            this.addSousGroupe(nouveau, cnx);
            //on ajoute les sous groupe des sous-groupe
        }
        rs.close();
      }catch(SQLException e){
        System.out.println(e);
        if(this.erreurCO())
            this.addSousGroupe(g, cnx);
      }
    }

    /**
   * Fonction recursive ajoutant les étudiant aux groupe de la promo
   * @param g le groupe pour qui ajouter les Etudiant
   * @param cnx la connection (evite de surcharger la BD)
   */
  private void addBDEtudiant(Groupe g, Connection cnx){
    try{
        //On récupère les etudiants contenue du groupe
        PreparedStatement pst;
        //Si c'est la promo
        pst= cnx.prepareStatement("SELECT Etudiant.nom, Etudiant.prenom, Etudiant.id FROM `CONTIENT` JOIN Etudiant on CONTIENT.idEt=Etudiant.id WHERE CONTIENT.idGroupe=? ORDER BY Etudiant.id ASC");
        pst.setInt(1, g.getId());
        ResultSet rs=pst.executeQuery();
        //Pour tous les étudiants
        while(rs.next()){
            boolean exist=false;
            //autrement on recupere l'etudiant
            for(Etudiant e: g.getPointPoint().getEtudiants()){
                if(e.getId()==rs.getInt(3)){
                    exist=true;
                    g.addEtudiant(e);
                    break;
                }
            }
            if(exist==false){
                g.addEtudiant(new EtudiantNP(rs.getString(1), rs.getString(2), rs.getInt(3)));
            }
        }
        rs.close();
        pst.close();
    }catch(SQLException e){
        System.out.println(e.toString());
      if(this.erreurCO())
        this.addBDEtudiant(g, cnx);
    }
  }




  /*******************
   * FONCTION POUR REFRESH
   * *******************
   */




  /**
   * Refresh le groupe donnée pour le mettre a jour avec la Base de donnée
   * Si le groupe n'existe plus on retourne le groupe parent le plus proche existant encore
   * @param g le groupe a refresh
	 * @param cnx la connection a la BD (evite de la surcharger)
   * @return le groupe refresh
   */
  private Groupe refreshGroupe(Groupe g, Connection cnx){
    try{
        PreparedStatement pst = cnx.prepareStatement(
        "SELECT `id`, `nom`, `min`, `max`, `value`, `id-parent` FROM `Groupe` join `TYPE` on Groupe.Type=TYPE.name where Groupe.`id`=? OR Groupe.`id-parent`=? ORDER BY Groupe.id ASC;");
        pst.setString(1, String.valueOf(g.getId()));
        pst.setString(2, String.valueOf(g.getId()));
        ResultSet rs=pst.executeQuery();
        if(rs.next()){
        if(rs.getString(2)!=g.getName()){
            g.setName(rs.getString(2));
        }
        Set<Groupe> it=new LinkedHashSet<>();
        for(Groupe gr:g.getSousGroupes()){
            it.add(gr);
        }
        for(Groupe gr:it){
            g.removeSousGroupe(gr);
        }
        Set<Etudiant> et=new LinkedHashSet<>();
        for(Etudiant gr:g.getEtudiants()){
            et.add(gr);
        }
        for(Etudiant gr:et){
            g.removeEtudiant(gr);
        }
        this.addSousGroupePasRecursif(g, cnx);
        }else{
        if(g==g.getPointPoint()){
            g=null;
        }else{
            g=this.refreshGroupe(g.getPointPoint(), cnx);
        }
        }
        rs.close();
    }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurCO())
            this.refreshGroupe(g, cnx);
    }
    return g;
  }

  /**
   * refresh le Groupe ainsi que ses etudiants
   * @param g le groupe
   * @return le groupe refresh/un parent si il a été refresh
   */
  public Groupe refreshALL(Groupe g){
    Connection cnx = this.cnx();
    g=this.refreshGroupe(g, cnx);
    Set<Etudiant> et=new LinkedHashSet<>();
    for(Etudiant gr:g.getEtudiants()){
      et.add(gr);
    }
    for(Etudiant gr:et){
      g.removeEtudiant(gr);
    }
    this.addBDEtudiant(g, cnx);
    this.close(cnx);
    return g;
  }


  /**
     * Fonction recursive permettant de récuperrer les sous groupe a partir de la BD
     * @param g le groupe
     * @param cnx la connection a la BD (evite de la surcharger)
     * @param pourcent le pourcentage de ce groupe dans le chargement
     */
    private void addSousGroupePasRecursif(Groupe g, Connection cnx){
        try{
          //On récupere les Groupe qui ont le parent :g
          PreparedStatement pst= cnx.prepareStatement(
            "SELECT `id`, `nom`, `min`, `max`, `value`, `id-parent` FROM `Groupe` join `TYPE` on Groupe.Type=TYPE.name where Groupe.`id-parent`=? and Groupe.id!=Groupe.`id-parent`;");
        pst.setString(1, String.valueOf(g.getId()));
        ResultSet rs=pst.executeQuery();
        rs=pst.executeQuery();
        //autrement si le groupe as des sous groupe
        while(rs.next()){
            //on les ajoute
            Groupe nouveau=new GroupeNP(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getInt(4), TypeGroupe.getType(rs.getString(5)), g);
            //Si one le connait pas
            if(this.brain.get(nouveau.getId())==null){
                this.brain.put(nouveau.getId(), nouveau);
            }
            g.addSousGroupe(nouveau);
            //on ajoute les sous groupe des sous-groupe
        }
        rs.close();
      }catch(SQLException e){
        System.out.println(e.toString());
        if(this.erreurCO())
            this.addSousGroupe(g, cnx);
      }
    }
}
