import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.lang.Math;

/**
 * Cette classe permet d'afficher les éléments UML du projet.
 */
public class UmlDiagram extends Composition implements Serializable {

    private UmlToolbar _umlToolbar;
    private Object _selected;
    private Object _hovered;
    private String _gripHovered = null;
    private String _gripSelected = null;
    private boolean _viewGrips = false;
    private int gripSize = 8;

    public static final String GRIP_N = "N";
    public static final String GRIP_NE = "NE";
    public static final String GRIP_E = "E";
    public static final String GRIP_SE = "SE";
    public static final String GRIP_S = "S";
    public static final String GRIP_SW = "SW";
    public static final String GRIP_W = "W";
    public static final String GRIP_NW = "NW";

    //Type de liens
    final static float dash[] = {5.0f};
    final static BasicStroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
    private int _shiftX;
    private int _shiftY;

    private Class _mainClass = null;
    private List<Class> _classes;
    private List<Link> _links;

    /**
     * Constructeur - Crée un nouveau diagramme UML à partir d'un projet.
     * @param project Le projet contenant ce diagramme UML.
     */
    public UmlDiagram(Project project){
        super(project);
        this._toolbar = new UmlToolbar(this);
        this._type = Composition.UML_DIAGRAM;
        this._name = "Composition " + Composition._compositionId + " (" + _type + ")";
        _classes = new ArrayList<Class>();
        _links = new ArrayList<Link>();
    }

    /**
     * Dessine les classes et les liens UML.
     * @param g L'objet graphique
     */
    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        //Activation de l'antialiasing pour les dessins
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.setRenderingHints(rh);

        Font font1 = new Font("Montserrat Light", Font.PLAIN, 14);
        FontMetrics metrics1 = this.getFontMetrics(font1);
        Font font2 = new Font("Montserrat Light", Font.PLAIN, 10);
        FontMetrics metrics2 = this.getFontMetrics(font2);

        g2.setColor(new Color(127, 158, 178));
        g2.drawString(_name, 10, 20);
        for(Class a : _classes){
            g2.setFont(font1);
            int posX = a.getPosX() - (a.getSizeX()/2);
            int posY = a.getPosY() - (a.getSizeY()/2);
            //Dessin du rectangle
            g2.setColor(new Color(127, 158, 178));
            g2.fillRect(posX, posY, a.getSizeX(), a.getSizeY());
            if(a == _mainClass) {
                g2.setColor(new Color(173, 37, 8));
                g2.drawRect(posX, posY, a.getSizeX(), a.getSizeY());
            }
            g2.setColor(Color.BLACK);
            //Dessin du nom de la classe
            g2.setColor(new Color(39, 76, 94));
            int posCounter = posY + font1.getSize();
            g2.drawString(a.getName(), posX + a.getSizeX()/2 - metrics1.stringWidth(a.getName())/2, posCounter);
            posCounter += 5;
            g2.setColor(new Color(218, 233, 244));
            //Ligne de séparation
            g2.drawLine(posX, posY + font1.getSize() + 5, posX + a.getSizeX()-1, posY + font1.getSize() + 5);
            g2.setFont(font2);
            posCounter += font2.getSize();
            g2.drawString("attributes", posX + a.getSizeX()/2 - metrics2.stringWidth("attributes")/2, posCounter);
            //Affichage des attributs
            g2.setColor(new Color(39, 76, 94));
            for(Attribute b : a.getAttributes()){
                posCounter += font2.getSize();
                g2.drawString(b.getAccess() + " " + b.getName() + " : " + b.getType(), posX+5, posCounter);
                //Si l'attribut est statique, on le souligne
                if(b.isStatic()) g2.drawLine(a.getPosX()-a.getSizeX()/2+10, posCounter+1, a.getPosX()+a.getSizeX()/2-10, posCounter+1);
            }
            posCounter += 5;
            g2.setColor(new Color(218, 233, 244));
            //Ligne de séparation
            g2.drawLine(posX, posCounter, posX + a.getSizeX()-1, posCounter);
            posCounter += font2.getSize();
            g2.drawString("methods", posX + a.getSizeX()/2 - metrics2.stringWidth("methods")/2, posCounter);
            //Dessin des méthodes
            for(Method m : a.getMethods()){
                posCounter += font2.getSize();
                //Si la méthode est un constructeur, on l'affiche en rouge, sinon en bleu
                System.out.println(a.getName());
                if(m.isConstructor()) g2.setColor(new Color(187, 11, 11));
                else g2.setColor(new Color(39, 76, 94));
                String chain = m.getAccess() + " " + m.getName() + "(";
                ArrayList<String> listArguments = new ArrayList<String>();
                for(Argument ar : m.getArguments()){
                    listArguments.add(ar.getName() + " : " + ar.getType());
                }
                chain += String.join(", ", listArguments) + ") : " + m.getType();
                g2.drawString(chain, posX+5, posCounter);
                //Si l'attribut est statique, on le souligne
                if(m.isStatic()) g2.drawLine(a.getPosX()-a.getSizeX()/2+10, posCounter+1, a.getPosX()+a.getSizeX()/2-10, posCounter+1);
            }
            //Affichage du lien temporaire rouge
            if(_selected != null && _gripSelected != null){
                Class b = (Class) _selected;
                System.out.println(b.getName());
                List<String> grips = Arrays.asList(GRIP_N, GRIP_NE, GRIP_E, GRIP_SE, GRIP_S, GRIP_SW, GRIP_W, GRIP_NW);
                List<List<Integer>> gripsPositions = b.getGripsPosition(gripSize);
                g2.setColor(Color.RED);
                g2.drawLine(gripsPositions.get(grips.indexOf(_gripSelected)).get(0) + gripSize/2, gripsPositions.get(grips.indexOf(_gripSelected)).get(1) + gripSize/2, this.getMousePosition().x, this.getMousePosition().y);
            }
            //Récupération de la liste des positions des points d'accroche pour la classe en cours
            List<List<Integer>> gripsPositions = a.getGripsPosition(gripSize);
            for(Link l : _links){
                List<String> grips = Arrays.asList(GRIP_N, GRIP_NE, GRIP_E, GRIP_SE, GRIP_S, GRIP_SW, GRIP_W, GRIP_NW);
                List<List<Integer>> gripsPositionsStart = l.getStart().getGripsPosition(gripSize);
                List<List<Integer>> gripsPositionsEnd = l.getEnd().getGripsPosition(gripSize);
                int startX = gripsPositionsStart.get(grips.indexOf(l.getGripStart())).get(0) + gripSize / 2;
                int startY = gripsPositionsStart.get(grips.indexOf(l.getGripStart())).get(1) + gripSize / 2;
                int endX = gripsPositionsEnd.get(grips.indexOf(l.getGripEnd())).get(0) + gripSize / 2;
                int endY = gripsPositionsEnd.get(grips.indexOf(l.getGripEnd())).get(1) + gripSize / 2;
                //Dessin du lien en fonction du type de la relation
                g2.setColor(Color.BLACK);
                g2.setStroke(new BasicStroke(1f));
                //Dessin de la ligne en pointillés si le lien est faible
                if(l.getType() == Link.WEAK){
                    g2.setStroke(dashed);
                    g2.drawLine(startX, startY, endX, endY);
                }
                else if (l.getType() == Link.STRONG) g2.drawLine(startX, startY, endX, endY);

                //Si lien de composition, d'agrégation ou d'héritage, on dessine soit un carré ou un triangle
                else{
                    //On crée le carré ou le rectangle
                    int[][] posGrip = {{startX, startY}};
                    //Détermination de l'angle pour que la figure s'adapte à la direction du lien
                    float angle = (float) Math.atan2(endX - startX, startY - endY);
                    //Détermination des points des polygones en fonction de l'angle et de la position du point d'accroche
                    float[][] posPol = getPolygonPoints(posGrip, -angle, l.getType());
                    int[] polygonX = new int[posPol.length];
                    int[] polygonY = new int[posPol.length];
                    for(int i = 0; i < posPol.length; i++){
                        polygonX[i] = (int)posPol[i][0];
                        polygonY[i] = (int)posPol[i][1];
                    }
                    //On dessine un polygone vide en agrégation
                    if(l.getType() == Link.AGGREGATION || l.getType() == Link.INHERITANCE) g2.drawPolygon(polygonX, polygonY, posPol.length);
                    //On dessine un polygone plein sinon
                    else g2.fillPolygon(polygonX, polygonY, posPol.length);
                    //Adaptation de la ligne sur le 3e point du polygone
                    g2.drawLine(polygonX[2], polygonY[2], endX, endY);
                }
                //Affichage des informations du lien
                if(l.getType() != Link.INHERITANCE){
                    //Décalage des positions pour mettre en évidence les écritures
                    if(l.getGripStart() == GRIP_NE || l.getGripStart() == GRIP_E || l.getGripStart() == GRIP_SE) startX += 20;
                    else if(l.getGripStart() == GRIP_NW || l.getGripStart() == GRIP_W || l.getGripStart() == GRIP_SW) startX -= 20;
                    else if(l.getGripStart() == GRIP_S) startY += 20;
                    if(l.getGripEnd() == GRIP_NE || l.getGripEnd() == GRIP_E || l.getGripEnd() == GRIP_SE) endX += 20;
                    else if(l.getGripEnd() == GRIP_NW || l.getGripEnd() == GRIP_W || l.getGripEnd() == GRIP_SW) endX -= 20;
                    else if(l.getGripEnd() == GRIP_S) endY += 20;
                    //Affichage des cardinalités sur la fin du lien
                    g2.drawString(l.getMinCardinalityEnd() + ".." + l.getMaxCardinalityEnd(), endX, endY);
                    if(l.getType() == Link.STRONG || l.getType() == Link.WEAK){
                        g2.drawString(l.getMinCardinalityStart() + ".." + l.getMaxCardinalityStart(), startX, startY);
                        g2.drawString(l.getName(), (startX + endX)/2, (startY + endY)/2);
                    }
                }
            }
            g2.setStroke(new BasicStroke(1f));
            //Affichage des points d'accroche
            if(_viewGrips || (a == (Class)_selected)) {
                //Changement de la couleur en fonction du mode
                g2.setColor((_viewGrips) ? Color.GREEN : new Color(39, 76, 94));
                //Top
                g2.fillOval(gripsPositions.get(0).get(0), gripsPositions.get(0).get(1), gripSize, gripSize);
                //Top Right
                g2.fillOval(gripsPositions.get(1).get(0), gripsPositions.get(1).get(1), gripSize, gripSize);
                //Right
                g2.fillOval(gripsPositions.get(2).get(0), gripsPositions.get(2).get(1), gripSize, gripSize);
                //Bottom Right
                g2.fillOval(gripsPositions.get(3).get(0), gripsPositions.get(3).get(1), gripSize, gripSize);
                //Bottom
                g2.fillOval(gripsPositions.get(4).get(0), gripsPositions.get(4).get(1),gripSize, gripSize);
                //Bottom Left
                g2.fillOval(gripsPositions.get(5).get(0), gripsPositions.get(5).get(1), gripSize, gripSize);
                //Left
                g2.fillOval(gripsPositions.get(6).get(0), gripsPositions.get(6).get(1), gripSize, gripSize);
                //Top Left
                g2.fillOval(gripsPositions.get(7).get(0), gripsPositions.get(7).get(1), gripSize, gripSize);
            }
        }
    }

    /**
     * Calcule les points du polygone à tracer.
     * @param position La position du point d'accroche.
     * @param type Le type de relation.
     * @return Les points du polygone à tracer.
     */
    public float[][] getPolygonPoints(int[][] position, double angle, int type){
        //Patterns de polygon : carré pour composition et agrégation, triangle pour héritage
        int[][] squarePattern = {{0,0},{-10,-10},{0,-20},{10,-10},{0,0}};
        int[][] trianglePattern = {{0,0},{-10,-20},{0,-20},{10,-20},{0,0}};

        //Demande d'un carré ou d'un triangle selon le type de lien
        float[][] points = null;
        switch(type){
            case Link.INHERITANCE:
                points = rotateMatrice(trianglePattern, angle);
                break;
            default:
                points = rotateMatrice(squarePattern, angle);
                break;
        }
        //Déplacement du pattern à la position du point d'accroche
        for(int i = 0; i < points.length; i++){
            points[i][0] += position[0][0];
            points[i][1] += position[0][1];
        }
        return points;
    }

    /**
     * Rotate le polygone sous forme de matrice.
     * @param matrice Les points du pattern de polygone.
     * @param angle L'angle de rotation.
     * @return La matrice tournée correspondant au polygone final.
     */
    public float[][] rotateMatrice(int[][] matrice, double angle){
        //Matrice de rotation en fonction de l'angle
        double[][] rotate = {{Math.cos(angle), (-1)*Math.sin(angle)},{Math.sin(angle), Math.cos(angle)}};
        //Matrice finale
        float[][] finalMatrice = new float[matrice.length][2];
        //Multiplication de rotate * finalMatrice
        for(int i=0; i < matrice.length; i++){
            for(int j=0; j < 2; j++){
                finalMatrice[i][j] = 0;
                for(int k=0; k<2 ;k++)
                {
                    finalMatrice[i][j] += matrice[i][k] * rotate[k][j];
                }
            }
        }
        return finalMatrice;
    }

    /**
     * Retourne si la vue des points d'accroches est activée pour le diagramme
     * @return true : la vue des points d'accroche est activée, false sinon.
     */
    public boolean viewGripsEnabled(){
        return _viewGrips;
    }

    /**
     * Active/Désactive la vue des points d'accroches pour les classes.
     * @param enabled La vue des points d'accroches pour les classes.
     */
    public void setViewGripsEnabled(boolean enabled){
        _viewGrips = enabled;
        this.repaint();
    }

    /**
     * Retourne la classe principale du diagramme.
     * @return La classe principale du diagramme.
     */
    public Class getMainClass(){
        return _mainClass;
    }

    /**
     * Paramétre la classe principale du diagramme.
     * @param a La nouvelle classe principale du diagramme.
     */
    public void setMainClass(Class a){
        _mainClass = a;
    }

    /**
     * Récupère la toolbar du diagramme.
     * @return La toolbar du diagramme.
     */
    public UmlToolbar getToolbar(){
        return (UmlToolbar) this._toolbar;
    }

    /**
     * Récupère toutes les classes du projet.
     * @return Les classes du projet.
     */
    public List<Class> getClasses(){
        return this._classes;
    }

    /**
     * Ajoute une classe au projet.
     * @param c La classe à ajouter.
     */
    public void addClass(Class c){
        this._classes.add(c);
        //On vient d'ajouter la 1ère classe, définition en classe principale
        if(_classes.size() == 1) _mainClass = c;
    }

    /**
     * Retire une classe du projet.
     * @param c La classe à retirer.
     */
    public void removeClass(Class c){
        //On supprime les liens associés à cette classe
        Iterator<Link> iter = _links.iterator();
        while(iter.hasNext()){
            Link link = iter.next();
            if(c.equals(link.getStart()) || c.equals(link.getEnd())) iter.remove();
        }
        //On supprime la classe principale. Par défaut, la 1ère classe créée devient la classe principale.
        this._classes.remove(c);
        if(_mainClass == c && _classes.size() != 0) _mainClass = _classes.get(0);
        _selected = null;
    }

    /**
     * Supprime toutes les classes du projet.
     */
    public void clearClasses(){
        this._classes.clear();
        this.clearLinks();
    }

    /**
     * Récupère tous les liens du projet.
     * @return Les liens du projet.
     */
    public List<Link> getLinks(){
        return this._links;
    }

    /**
     * Ajoute un lien au projet.
     * @param l Le lien à ajouter.
     */
    public void addLink(Link l){
        this._links.add(l);
    }

    /**
     * Retire un lien du projet.
     * @param l Le lien à retirer.
     */
    public void removeLink(Link l){
        this._links.remove(l);
    }

    /**
     * Retire un lien du projet.
     * @param index L'index du lien à retirer.
     */
    public void removeLink(int index){
        this._links.remove(index);
    }

    /**
     * Supprime tous les liens du projet.
     */
    public void clearLinks(){
        this._links.clear();
    }

    //Evenements souris
    /**
     * Récupère l'élément sélectionné dans la grille.
     * @return L'élément sélectionné dan sla grille.
     */
    public Object getSelected(){
        return this._selected;
    }

    /**
     * Récupération de l'objet cliqué
     * @param getX Les coordonnées de la souris sur l'axe X.
     * @param getY Les coordonnées de la souris sur l'axe Y.
     */
    public void select(int getX, int getY){
        //Si un élément est survolé, on le sélectionne
        for(Class c : _classes){
            int posX = c.getPosX();
            int posY = c.getPosY();
            int sizeX = c.getSizeX();
            int sizeY = c.getSizeY();
            //Si cette classe est survolée, on affiche son nom dans la barre de statut et on la sauvegarde temporairement
            _selected = null;
            if(getX >= posX - sizeX/2 - gripSize && getX <= posX + sizeX/2 + gripSize && getY >= posY - sizeY/2 - gripSize && getY <= posY + sizeY/2 + gripSize){
                AppThinker.getWindow().getStatusbar().setStatusMessage("Selected class : " + c.getName());
                _selected = c;
                //Si on est en mode édition de liens, on recherche le point d'accroche survolé
                if(_viewGrips) {
                    checkHoveredGrip((Class) _selected, getX, getY);
                    _gripSelected = _gripHovered;
                }
                break;
            }
        }
        //Si la classe est sélectionnée, on enregistre le décalage entre la souris et le centre de la classe
        if(_selected instanceof Class){
            Class a = (Class) _selected;
            AppThinker.getWindow().getStatusbar().setSizeLabel(a.getSizeX(), a.getSizeY());
            _shiftX = getX - a.getPosX();
            _shiftY = getY - a.getPosY();
        }
    }

    /**
     * Modifie les propriétés de la classe.
     * @param a La classe a modifier.
     */
    public void editClass(Class a){
        ClassPropertiesWindow prop = new ClassPropertiesWindow(this, a);
    }

    /**
     * Vérifie si le curseur est à proximité d'un point d'accroche. Dans ce cas, il enregistre le point survolé.
     * @param a La classe à vérifier.
     * @param x L'abscisse du curseur.
     * @param y L'ordonnée du curseur.
     */
    public void checkHoveredGrip(Class a, int x, int y){
        int sensibility = 5;
        int posX = a.getPosX();
        int posY = a.getPosY();
        int sizeX = a.getSizeX();
        int sizeY = a.getSizeY();
        //Si la souris est à proximité d'un point d'accroche, on affiche le curseur de redimensionnement
        if (x >= posX - sensibility && x <= posX + sensibility && y >= posY - sizeY/2 - sensibility && y <= posY - sizeY/2 + sensibility) {
            _gripHovered = GRIP_N;
            this.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));
        } else if (x >= posX + sizeX/2 - sensibility && x <= posX + sizeX/2 + sensibility && y >= posY - sizeY/2 - sensibility && y <= posY - sizeY/2 + sensibility) {
            _gripHovered = GRIP_NE;
            this.setCursor(new Cursor(Cursor.NE_RESIZE_CURSOR));
        } else if (x >= posX + sizeX/2 - sensibility && x <= posX + sizeX/2 + sensibility && y >= posY - sensibility && y <= posY + sensibility) {
            _gripHovered = GRIP_E;
            this.setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
        } else if (x >= posX + sizeX/2 - sensibility && x <= posX + sizeX/2 + sensibility && y >= posY + sizeY/2 - sensibility && y <= posY + sizeY/2 + sensibility) {
            _gripHovered = GRIP_SE;
            this.setCursor(new Cursor(Cursor.SE_RESIZE_CURSOR));
        } else if (x >= posX - sensibility && x <= posX + sensibility && y >= posY + sizeY/2 - sensibility && y <= posY + sizeY/2 + sensibility) {
            _gripHovered = GRIP_S;
            this.setCursor(new Cursor(Cursor.S_RESIZE_CURSOR));
        } else if (x >= posX - sizeX/2 - sensibility && x <= posX - sizeX/2 + sensibility && y >= posY + sizeY/2 - sensibility && y <= posY + sizeY/2 + sensibility) {
            _gripHovered = GRIP_SW;
            this.setCursor(new Cursor(Cursor.SW_RESIZE_CURSOR));
        } else if (x >= posX - sizeX/2 - sensibility && x <= posX - sizeX/2 + sensibility && y >= posY - sensibility && y <= posY + sensibility) {
            _gripHovered = GRIP_W;
            this.setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
        } else if (x >= posX - sizeX/2 - sensibility && x <= posX - sizeX/2 + sensibility && y >= posY - sizeY/2 - sensibility && y <= posY - sizeY/2 + sensibility) {
            _gripHovered = GRIP_NW;
            this.setCursor(new Cursor(Cursor.NW_RESIZE_CURSOR));
        } else {
            _gripHovered = null;
            this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        }
    }

    //Actions de la souris sur le diagramme UML
    /**
     * Action du clic de la souris sur le diagramme.
     * @param e L'événement souris.
     */
    @Override
    public void mouseClicked(MouseEvent e) {
        //Si il s'agit d'un double-clic et que l'outil sélection est sélectionné, on ouvre la fenêtre d'édition de la classe
        UmlToolbar toolbar = (UmlToolbar) _toolbar;
        if(e.getClickCount() == 2 && toolbar.getCurrentTool() == UmlToolbar.SELECT_TOOL){
            if(_selected instanceof Class){
                this.editClass((Class) _selected);
            }
        }
    }

    /**
     * Bouton de la souris pressé sur la grille. On récupère l'outil sélectionné pour parvenir à l'action.
     * @param e L'événement souris.
     */
    @Override
    public void mousePressed(MouseEvent e) {
        UmlToolbar toolbar = (UmlToolbar) _toolbar;
        int tool = toolbar.getCurrentTool();
        switch(tool){
            //On essaie de sélectionner un élément
            case UmlToolbar.EDIT_TOOL:
                System.out.println("On édite un élément.");
                this.select(e.getX(), e.getY());
                if(_selected instanceof Class) this.editClass((Class) _selected);
                break;
            case UmlToolbar.DELETE_TOOL:
                System.out.println("On supprime un élément.");
                this.select(e.getX(), e.getY());
                if(_selected instanceof Class) removeClass((Class)_selected);
                break;
            case UmlToolbar.COPY_TOOL:
                System.out.println("On copie un élément.");
                break;
            case UmlToolbar.PASTE_TOOL:
                System.out.println("On colle un élément.");
                break;
            //On essaie d'ajouter une classe
            case UmlToolbar.CLASS_TOOL:
                this.addClass(new Class(e.getX(), e.getY()));
                break;
            case UmlToolbar.STRONG_TOOL:
                System.out.println("On ajoute une relation forte.");
                this.select(e.getX(), e.getY());
                break;
            case UmlToolbar.WEAK_TOOL:
                System.out.println("On ajoute une relation faible.");
                this.select(e.getX(), e.getY());
                break;
            case UmlToolbar.COMPOSITION_TOOL:
                System.out.println("On ajoute une relation de composition.");
                this.select(e.getX(), e.getY());
                break;
            case UmlToolbar.AGGREGATION_TOOL:
                System.out.println("On ajoute une relation d'agrégation.");
                this.select(e.getX(), e.getY());
                break;
            case UmlToolbar.INHERITANCE_TOOL:
                System.out.println("On ajoute une relation d'héritage.");
                this.select(e.getX(), e.getY());
                break;
            default:
                this.select(e.getX(), e.getY());
                break;
        }
        this.repaint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        //Si on lâche le curseur en cours de création de liens, on annule la saisie.
        if(_viewGrips){
            if(_gripSelected != null){
                _gripSelected = null;
                _selected = null;
                this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            }
            this.repaint();
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    /**
     * Déplacer un élément en cliquant et déplaçant la souris.
     * @param e Evénement souris
     */
    @Override
    public void mouseDragged(MouseEvent e) {
        //Mise à jour des coordonnées de la souris dans la statusbar
        AppThinker.getWindow().getStatusbar().setPosLabel(e.getX(), e.getY());
        int posX = e.getX();
        int posY = e.getY();
        if(_selected instanceof Class){
            Class a = (Class)_selected;
            int shiftX = 0;
            int shiftY = 0;
            //Sinon, soit on redimensionne une classe ou on la déplace.
            if(!_viewGrips){
                if(_gripHovered != null){
                    switch(_gripHovered){
                        case GRIP_N:
                            a.resizeUp(posY);
                            break;
                        case GRIP_NE:
                            a.resizeUp(posY);
                            a.resizeRight(posX);
                        case GRIP_E:
                            a.resizeRight(posX);
                            break;
                        case GRIP_SE:
                            a.resizeDown(posY);
                            a.resizeRight(posX);
                        case GRIP_S:
                            a.resizeDown(posY);
                            break;
                        case GRIP_SW:
                            a.resizeDown(posY);
                            a.resizeLeft(posX);
                            break;
                        case GRIP_W:
                            a.resizeLeft(posX);
                            break;
                        case GRIP_NW:
                            a.resizeUp(posY);
                            a.resizeLeft(posX);
                            break;
                    }
                    AppThinker.getWindow().getStatusbar().setSizeLabel(a.getSizeX(), a.getSizeY());
                }
                //Sinon on déplace
                else{
                    //On repositionne la classe en prenant en compte le décalage mesuré au clic de la souris
                    a.setPosX(posX - _shiftX);
                    a.setPosY(posY - _shiftY);
                    this.setCursor(new Cursor(Cursor.MOVE_CURSOR));
                }
            }
        }
        //On est en train de créer un lien, on recherche le point d'accroche d'arrivée
        if(_selected != null && _gripSelected != null){
            Class selected = (Class) _selected;
            for(Class hovered : _classes){
                int posXSelected = hovered.getPosX();
                int posYSelected = hovered.getPosY();
                int sizeXSelected = hovered.getSizeX();
                int sizeYSelected = hovered.getSizeY();
                //Si cette classe est survolée, on affiche son nom dans la barre de statut et on la sauvegarde temporairement
                _hovered = null;
                if(posX >= posXSelected - sizeXSelected/2 - gripSize && posX <= posXSelected + sizeXSelected/2 + gripSize && posY >= posYSelected - sizeYSelected/2 - gripSize && posY <= posYSelected + sizeYSelected/2 + gripSize){
                    AppThinker.getWindow().getStatusbar().setStatusMessage("Approach a target grip on this class to create the link : " + hovered.getName());
                    if(hovered != _selected){
                        _hovered = hovered;
                        //On recherche le point survolé
                        checkHoveredGrip((Class) _hovered, posX, posY);
                        //Si toutes les conditions sont réunies, on crée le lien
                        if(_hovered != null && _gripHovered != null && _selected != null && _gripSelected != null){
                            //Récupération du type de lien
                            UmlToolbar toolbar = (UmlToolbar) _toolbar;
                            int currentTool = toolbar.getCurrentTool();
                            int type = (currentTool == UmlToolbar.STRONG_TOOL) ? Link.STRONG : (currentTool == UmlToolbar.WEAK_TOOL) ? Link.WEAK : (currentTool == UmlToolbar.COMPOSITION_TOOL) ? Link.COMPOSITION : (currentTool == UmlToolbar.AGGREGATION_TOOL) ? Link.AGGREGATION : Link.INHERITANCE;
                            _links.add(new Link((Class) _selected, (Class) _hovered, _gripSelected, _gripHovered, 0, 0, 0, 0, type));
                            _selected = null;
                            _gripSelected = null;
                            _gripHovered = null;
                            _hovered = null;
                            this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
                        }
                    }
                    break;
                }
            }
        }
        this.repaint();
    }

    /**
     * Déclenchée par le mouvement de la souris, cette fonction permet de récupérer la classe actuellement survolée.
     * @param e L'événement souris.
     */
    @Override
    public void mouseMoved(MouseEvent e) {
        int getX = e.getX();
        int getY = e.getY();
        int sens = 5;
        //Mise à jour des coordonnées de la souris dans la statusbar
        AppThinker.getWindow().getStatusbar().setPosLabel(e.getX(), e.getY());
        //Si une classe est sélectionnée, on recherche si le curseur est à proximité d'un point d'accroche
        if (_selected instanceof Class) {
            Class a = (Class) _selected;
            AppThinker.getWindow().getStatusbar().setSizeLabel(a.getSizeX(), a.getSizeY());
            //Si une classe est sélectionnée, on recherche si le curseur est à proximité d'un point d'accroche
            checkHoveredGrip(a, getX, getY);
        }
    }
}
