package fr.iutfbleau.projetIHM2022FI2.MNP.ROOT.Model;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
import java.awt.*;

import fr.iutfbleau.projetIHM2022FI2.API.AbstractChangementFactory;
import fr.iutfbleau.projetIHM2022FI2.API.AbstractGroupeFactory;
import fr.iutfbleau.projetIHM2022FI2.API.Changement;
import fr.iutfbleau.projetIHM2022FI2.API.Etudiant;
import fr.iutfbleau.projetIHM2022FI2.API.Groupe;
import fr.iutfbleau.projetIHM2022FI2.API.Model;
import fr.iutfbleau.projetIHM2022FI2.API.TypeGroupe;
import fr.iutfbleau.projetIHM2022FI2.MNP.AbstractChangementFactoryNP;
import fr.iutfbleau.projetIHM2022FI2.MNP.AbstractGroupeFactoryNP;
import fr.iutfbleau.projetIHM2022FI2.MP.ROOT.View.FenetreChangement;
import fr.iutfbleau.projetIHM2022FI2.MP.ROOT.View.FenetreEtudiant;
import fr.iutfbleau.projetIHM2022FI2.MP.ROOT.View.FenetreGroupe;
import fr.iutfbleau.projetIHM2022FI2.MP.ROOT.View.FindBarFenetre;
import fr.iutfbleau.projetIHM2022FI2.Permanent.Controller.ObservateurChangeGroupe;
import fr.iutfbleau.projetIHM2022FI2.Permanent.Controller.ObservateurFenetre;
import fr.iutfbleau.projetIHM2022FI2.Permanent.View.Chargement;

/**
 * Le Model de L'IHM
 */
public class ModelRoot implements Model{

	// la fenetre de groupe
  private FenetreGroupe fenGr;
	//la fenetre d'etudiant
  private FenetreEtudiant fenEtu;
	// le groupe courant
  private AbstractGroupeFactory promo;
	// le changement courant
  private AbstractChangementFactory changement;
	// la fenetre principale
  private JFrame fenetre; 
  //fenetre du changement
  private FenetreChangement fenChange;
	
	/**
	 * Constructeur du model.
	 * @param agf le groupe courant
	 * @param change le changement courant
	 */
  public ModelRoot(AbstractGroupeFactory agf, AbstractChangementFactory change){
    this.fenetre=new JFrame();
    this.fenetre.setSize(1200, 720);
    this.fenetre.setLocation(100,100);
    this.fenetre.addWindowListener(new ObservateurFenetre());
    this.fenetre.setLayout(new GridLayout(1,2));
    this.fenetre.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    this.fenetre.setMinimumSize(this.fenetre.getSize());
    Chargement ch=new Chargement(this.fenetre);
    this.promo=agf;
    this.changement=change;
    ch.dispose();
    this.fenetre.setVisible(true);
    if(this.promo.getPromotion()!=null){
      this.showGroupe(this.promo.getPromotion());
    }else{
      this.showGroupe(null);
    }
    this.fenChange=null;
  }

  @Override
  /**
   * Fonction pour refresh/changer de groupe d'affichage
   * @param g le groupe a afficher
   */
  public void showGroupe(Groupe g){
    if(g!=null)
      g=this.promo.refreshALL(g);
      this.fenetre.getContentPane().removeAll();
    this.fenGr=new FenetreGroupe(g, this);
    this.fenEtu=new FenetreEtudiant(g, this);
    this.fenetre.getContentPane().removeAll();
    this.fenetre.add(this.fenGr.getPan());
    JScrollPane scroll=new JScrollPane(this.fenEtu.getPan());
    scroll.getVerticalScrollBar().setUnitIncrement(15);
    this.fenetre.add(scroll);
    this.fenetre.revalidate();
  }
  
  @Override 
  /**
   * getteur de la fenetre
   * @return JFrame la fenetre
   */
  public JFrame getFenetre() {
      return fenetre;
  }
  /**
	 * permet d'obtenir l'ensemble des etudiants
	 * @return
	 */
  @Override
  public Set<Etudiant> getEtudiant(){
    return this.promo.getEtudiants(null);
  }

   // ***********************
  // FONCTION POUR SUPRIMER UN ETUDIANT
  // ***********************
/**
 * Fonction pour supprimer un etudiant.
 * @param e l'etudiant a supprimer
 * @return boolean true si l'etudiant a ete supprimer, false sinon
 */
  @Override
  public boolean deleteEtu(Etudiant e){
    if(this.deleteEtutoChildren(e, this.fenGr.getG())){
      this.promo.dropFromGroupe(this.fenGr.getG(), e);
      this.showGroupe(this.fenGr.getG());
      return true;
    }else{
      return false;
    }
  }
/**
 * Permet de savoir si un étudiant peut être supprimé
 * @param e l'étudiant à supprimer
 * @param g le groupe dans lequel on cherche
 * @return true si possible, false sinon
 */
  private boolean deleteEtutoChildren(Etudiant e, Groupe g){
    if(g.getMax()<g.getEtudiants().size()-1)
      return false;
    for(Groupe sous: g.getSousGroupes()){
      if(this.deleteEtutoChildren(e, sous)==false){
        return false;
      }
    }
    return true;
  }


  // ***********************
  // FONCTION POUR Ajouter UN ETUDIANT
  // ***********************
  @Override
  /**
   * Fonction pour ajouter un étudiant au groupe
   * @param g
   * @param e
   * @return boolean pour savoir si il a pus être ajouter
   */
  public boolean addEtudiant(Groupe g, Etudiant e){
    //Si on a la place
    if(g!=g.getPointPoint())
      if(this.addEtuToParent(g.getPointPoint(), e)==false)
       return false;
    //On induqe a la BD de sauvegarder cette modification
    this.promo.addToGroupe(g, e);
    return true;
  }


  /**
   * Fonction recursive pour ajouter l'etudiant a ses parents si il ne l'ont pas
   * @param g groupe
   * @param e etudiant
   * @return true si possible
   */
  private boolean addEtuToParent(Groupe g, Etudiant e){
    if(g.getMax()==g.getSize())
      return false;
    for(Etudiant et:g.getEtudiants()){
      if(et.getId()==e.getId()){
        return true;
      }
    }
    if(g.getPointPoint()!=g){
      if(this.addEtuToParent(g.getPointPoint(), e)==false){
        return false;
      }
    }
    this.promo.addToGroupe(g, e);
    return true;
  }


  // ***********************
  // FONCTION POUR SUPRIMER UN GROUPE
  // ***********************
  @Override
  /**
   * Fonction pour supprimer un groupe
   * @param g le groupe a supprimer
   */
  public void delete(Groupe g){
    //On ne peut pas supprimer la promo normallement
    if(g==this.promo.getPromotion()){
      if(JOptionPane.showConfirmDialog(this.fenetre, "Attention ête vous sûr de vouloir supprimer la promo", "Attention", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){
        this.promo.deleteGroupe(this.promo.getPromotion());
        this.showGroupe(null);
      }else{
        return;
      }
      //JOptionPane.showMessageDialog(this.fenetre, "impossible de supprimer la promotion", "alerte", JOptionPane.ERROR_MESSAGE);
    }else{
      //on suprime le groupe
      this.promo.deleteGroupe(g);
      this.showGroupe(g.getPointPoint());
    }
  }

  // ***********************
  // FONCTION POUR RENOMER UN GROUPE
  // ***********************

  @Override
   /**
   * Fonction pour renomer un groupe
   * @param name
   * @param g
   */
  public void rename(String name, Groupe g){
    this.promo.changeNameGroupe(g, name);
    this.showGroupe(g);
  }


  // **********************
  // FONCTION POUR CREER UN GROUPE
  // **********************

  @Override
  /**
   * Fonction permetant de créer une partition d'un groupe
   * @param g le groupe a partitionner
   * @param n le nombre de partition
   * @param name le nom des partition
   */
  public void partition(Groupe g, int n, String name){
    Chargement ch=new Chargement(this.fenetre);
    this.fenetre.setVisible(false);
    this.promo.createPartition(g, name, n);
    ch.dispose();
    this.fenetre.setVisible(true);
    this.showGroupe(g);
  }

  @Override
  /**
   * Fonction permettant de créer un sous-groupe d'un groupe
   * @param g le groupe parent
   * @param name le nom du sous-groupe
   * @param min le nombre min d'etudiant
   * @param max le nombre max d'etudiant
   * @param ajout la liste des étudiants a ajouter au groupe
   */
  public void free(Groupe g, String name, int min, int max, Set<Etudiant> ajout){
    Chargement ch=new Chargement(this.fenetre);
    this.fenetre.setVisible(false);
    //on creer le groupe
    this.promo.createGroupe(g, name, min, max);
    Groupe creer=null;
    //on le recupere
    for(Groupe gr:g.getSousGroupes()){
      if(gr.getName()==name){
        creer=gr;
        break;
      }
    }
    //on y ajoute les étudiant
    int n=0;
    for(Etudiant e:ajout){
      if(this.addEtudiant(creer, e)==false)
        n++;
    }
    creer=this.promo.refreshALL(creer);
    ch.dispose();
    this.fenetre.setVisible(true);
    if(n>0)
      JOptionPane.showMessageDialog(this.fenetre, "erreur impossible d'ajouter "+n+" etudiants par manque de place dans le groupe de ses parents", "erreur", JOptionPane.ERROR_MESSAGE);
    //Si finalement avec les etudiants qui n'ont pas pus être ajouter le min n'est pas atteint
    if(creer.getEtudiants().size()+1<=creer.getMin()){
      //On le supprime
      this.promo.deleteGroupe(creer);
      JOptionPane.showMessageDialog(this.fenetre, "en raison du manque d'etudiant pour le nombre min le groupe n'a pas pus être créer", "errer", JOptionPane.ERROR_MESSAGE);
    }
    this.showGroupe(g);
  }
  /**
   * ajoute une nouvelle promotion
   * @param min le nombre min
   * @param max le nombre max
   * @param name le nom
   * @param ajout les etudiant a ajouter a la promo
   */
  @Override
  public void addPromo(int min, int max, String name, Set<Etudiant> ajout){
    Chargement ch=new Chargement(this.fenetre);
    this.promo=new AbstractGroupeFactoryNP(name, min, max);
    this.changement=new AbstractChangementFactoryNP(this.promo);
    this.fenetre.setVisible(false);
    for(Etudiant e:ajout){
      this.addEtudiant(this.promo.getPromotion(), e);
    }
    ch.dispose();
    this.fenetre.setVisible(true);
    this.showGroupe(this.promo.getPromotion());
  }


  /**
   * Créer un changement depuis le groupe courant
   * @param e l'etudiant
   * @param b le groupe a changer
   */
  @Override
  public void changeGroupe(Etudiant e, Groupe b){
    if(b==null)
      return;
    b=this.promo.refreshALL(b);
    if(b.getEtudiants()!=null && b.getMax()>b.getEtudiants().size()+1){
      if(b.getEtudiants().size()>this.fenGr.getG().getEtudiants().size()){
        JPanel myPanel= new JPanel(new GridLayout(2,1));
        JTextField xField = new JTextField(100);
        xField.setMinimumSize(new Dimension(100, 100));
        xField.setPreferredSize(new Dimension(100,100));
        myPanel.add(new JLabel("Ce Groupe est plus grand que le votre raison du changement:"));
        myPanel.add(xField);
        if(JOptionPane.showConfirmDialog(this.fenetre, myPanel, "Changer dans quel Groupe ?", JOptionPane.OK_CANCEL_OPTION)==JOptionPane.OK_OPTION){
          //IL faut que l'etudiant detaile un minimun la raison
          if(xField.getText().length()>5){
            this.changement.createChangement(this.fenGr.getG(), e, b, xField.getText());
          }else{
            JOptionPane.showMessageDialog(this.fenetre, "veuillez detailler votre raison", "erreur", JOptionPane.ERROR_MESSAGE);
          }
        }
      }else{
        this.changement.createChangement(this.fenGr.getG(), e, b);
      }
    }else{
      JOptionPane.showMessageDialog(this.fenetre, "impossible trop d'etudiant dans l'autre Groupe", "erreur", JOptionPane.ERROR_MESSAGE);
    }
    this.showGroupe(this.fenGr.getG());
  }
    /**
     * Retourn les groupe auquel on  peut demander a changer depuis le groupe courant
     *
     * @return la liste des groupes
     */
  @Override
  public Set<Groupe> getGroupePartition(){
    this.promo.refreshALL(this.fenGr.getG().getPointPoint());
    Set<Groupe> retour=new HashSet<Groupe>();
    if(this.fenGr.getG().getPointPoint().getType()!=TypeGroupe.PARTITION)
      throw new IllegalStateException("impossible de changer un étudiant d'un groupe ne provenant pas d'une partition");
    for(Groupe sous:this.fenGr.getG().getPointPoint().getSousGroupes()){
      if(sous.getId()!=this.fenGr.getG().getId()){
        retour.add(sous);
      }
    }
    return retour;
  }

  /* (non-Javadoc)
   * @see fr.iutfbleau.projetIHM2022FI2.API.Model#listChange()
   */
	  /**
   * Fonction pour afficher une fenetre listnatn les changements
   * 
   */
  @Override
  public void listChange() {
    this.fenChange=new FenetreChangement(changement, this);
    JOptionPane.showMessageDialog(this.fenetre, new JScrollPane(this.fenChange), "liste des changements", JOptionPane.OK_OPTION);
  }
  /**
   * Fonction pour effectuer un changement
   * @param Changement le changement a effecteur
   */
  @Override
  public void change(Changement change) {
    this.changement.applyChangement(change);
    this.fenChange.refresh();
    if(change.getA()==this.fenGr.getG()){
      this.showGroupe(this.fenGr.getG());
    }
  }

  /**
   * Fonction supprimant un changement
   * 
   * @param to_delete la changement a delete
   */
  @Override
  public void deleteChange(Changement to_delete) {
    this.changement.deleteChangement(to_delete);
    this.fenChange.refresh();
  }

  /**
   * Fonction affichant les raison d'un changement
   * 
   * @param change le changement
   */
  @Override
  public void showRaisonChange(Changement change) {
    JOptionPane.showMessageDialog(this.fenetre, new JLabel(change.getRaison()), "Raison de la demande", JOptionPane.OK_OPTION);
  }
  /**
   * Fonction affichant une fentre pour trouver un etudiant et tous ses groupes asociée
   */
  @Override
  public void findEtu() {
    JScrollPane scroll=new JScrollPane(new FindBarFenetre(this).getPanel());
    scroll.getVerticalScrollBar().setUnitIncrement(5);
    JOptionPane.showMessageDialog(this.fenetre, scroll, "chercher un etudiant", JOptionPane.OK_OPTION);
  }

  /* (non-Javadoc)
   * @see fr.iutfbleau.projetIHM2022FI2.API.Model#getEtu(java.lang.String)
   */
	
  /**
   * Fonction pour recuperer les etudiant avec ce nom
   * @param name le nom de(s) etudiants
   * @return la liste
   */
  @Override
  public Set<Etudiant> getEtu(String name) {
    return this.promo.getEtudiants(name);
  }
  /**
   *affiche les groupe d'un etudiant
   * @param e l'etudiant
   */
  @Override
  public void showGroupOfEtudiant(Etudiant e){
    Set<Groupe> liste=this.promo.getGroupesOfEtudiant(e);
    JPanel myPanel;
    if(liste!=null && liste.size()>0){
      myPanel=new JPanel(new GridLayout(liste.size()/2+1, 2, 20, 20));
      for(Groupe g:liste){
        JButton bout=new JButton(g.getName());
        bout.addActionListener(new ObservateurChangeGroupe(this, g));
        myPanel.add(bout);
      }
    }else{
      myPanel=new JPanel(new GridLayout(2, 2, 20, 20));
      myPanel.add(new JLabel("Cet Etudiant  n'appartient encore a aucun groupe"));
    }
    JOptionPane.showMessageDialog(this.fenChange, myPanel, "groupe de l'etudiant", JOptionPane.OK_OPTION);
  }

}
