/*
Copyright 2005-2013 Samuel Gesche

This file is part of ArcEnCiel.

ArcEnCiel 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 3 of the License, or
(at your option) any later version.

ArcEnCiel 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 ArcEnCiel.  If not, see <http://www.gnu.org/licenses/>.
*/

package ihm.townto;

import ihm.Charte;

import javax.swing.JComponent;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;

/**
 * Cette classe est le LayoutManager du GraphPanel. Elle agence les composants
 * suivant une disposition de type champ de forces : les composants reliés
 * s'attirent, mais tous les composants se repoussent. Le graphe ainsi agencé
 * est donc normalement bien lisible.<br>
 *
 * @author Samuel GESCHE
 * @version 3.0
 * @since 3.0.0
 */

public class GraphLayout implements LayoutManager {
    private int tooNear1 = 10*Charte.getFont().getSize();
    private int tooFar1 = 2*tooNear1;
    private int tooNear2 = 3*Charte.getFont().getSize();
    private int tooFar2 = 3*tooNear2;
    private int tooNear3 = 3*Charte.getFont().getSize();
    private int tooFar3 = tooNear3;
    private static int FONT_SIZE = Charte.getFont().getSize();
    private int n_font = FONT_SIZE;
    private boolean defined = false;
    private long start = 0;

    private boolean activated = true;

    /**
     * Crée un gestionnaire de disposition de classe GraphLayout.
     * @since 3.0.0
     */
    public GraphLayout() {
    }

    /**
     * Définit si la disposition doit être recalculée.
     * @param active boolean
     * @since 3.5.1
     */
    public void setActive(boolean active){
        this.activated = active;
        if(active){
            start = System.currentTimeMillis();
        }
    }

    /**
     * Ne fait strictement rien.
     * @param name String
     * @param comp Component
     * @since 3.0.0
     */
    public void addLayoutComponent(String name, Component comp) {

    }

    /**
     * Ne fait strictement rien.
     * @param comp Component
     * @since 3.0.0
     */
    public void removeLayoutComponent(Component comp) {

    }

    /**
     * Calcule la taille préférée du conteneur spécifié, au vu des composants
     * qu'il contient.
     * @param parent Container le conteneur en question
     * @return Dimension la taille préférée de ce conteneur
     * @since 3.0.0
     */
    public Dimension preferredLayoutSize(Container parent) {
        int x = 0;
        int y = 0;
        for (int i = 0; i < parent.getComponentCount(); i++) {
            int nx = parent.getComponent(i).getX() +
                     parent.getComponent(i).getWidth();
            int ny = parent.getComponent(i).getY() +
                     parent.getComponent(i).getHeight();
            if (nx > x) x = nx;
            if (ny > y) y = ny;
        }
        return new Dimension(x, y);
    }

    /**
     * Calcule la taille minimale du conteneur spécifié, au vu des composants
     * qu'il contient.
     * @param parent Container le conteneur en question
     * @return Dimension la taille minimale de ce conteneur
     * @since 3.0.0
     */
    public Dimension minimumLayoutSize(Container parent) {
        return new Dimension(0, 0);
    }

    /**
     * Déclare cet agencement invalide (l'agencement reprendra à zéro).
     * @since 3.0.0
     */
    public void invalidate() {
        defined = false;
    }

    /**
     * Agence les composants du conteneur spécifié. En réalité, effectue une
     * itération de cet agencement, donc des appels multiples sont nécessaires
     * pour animer le graphe.
     * @param parent Container le conteneur en question
     * @since 3.0.0
     */
    public void layoutContainer(Container parent) {
        if (parent instanceof GraphPanel) {
            if(activated){
                if (!defined) {
                    computeLocations((GraphPanel) parent);
                    defined = true;
                } else {
                    quake((GraphPanel) parent);
                }
            } else {
                redraw((GraphPanel) parent);
            }
        }
    }

    /**
     * Calcule des emplacements initiaux pour les noeuds du GraphPanel, de
     * manière heuristique.
     * @param component GraphPanel le GraphPanel en question
     * @since 3.0.0
     */
    protected void computeLocations(GraphPanel component) {
        start = System.currentTimeMillis();
        // Computing the constants
        if (component.getSecondCrown().length == 0) {
            tooNear1 = 50 + 20 * component.getActiveNode().getLineCount();
            tooFar1 = 10 * component.getFirstCrown().length +
                      20 * component.getActiveNode().getLineCount();
        } else {
            tooNear1 = 150;
            tooFar1 = 200;
        }

        final int W = component.getWidth();
        final int H = component.getHeight();
        final int w = component.getActiveNode().getWidth();
        final int h = component.getActiveNode().getHeight();
        final int dw = w / 2;
        final int r3 = tooNear3;
        final int r2 = tooNear2;
        final int r1 = tooNear1;
        final int x0 = (W - w) / 2 + dw;
        final int y0 = (H - h) / 2;

        // Computing locations
        // active node
        component.getActiveNode().setLocation(x0, y0);
        component.getActiveNode().setSize(component.getActiveNode().
                                          getPreferredSize());
        component.getActiveNode().setFontSize((int) (n_font * 1.2));
        component.getActiveNode().setBold();

        // crowns
        for (int i = 0; i < component.getFirstCrown().length; i++) {
            GraphPanel.Node cmp = component.getFirstCrown()[i];
            GraphPanel.NumberNode num = component.getNumberNode(cmp);
            GraphPanel.Node[] cmp2 = component.getSecondCrown(cmp);

            double a = 2 * Math.PI / component.getFirstCrown().length;
            int dwi = component.getActiveNode().getWidth() - cmp.getWidth();
            int dhe = component.getActiveNode().getHeight() - cmp.getHeight();
            cmp.setLocation((int) (x0 + dwi / 2 + r1 * Math.cos(i * a)),
                            (int) (y0 + dhe / 2 + r1 * Math.sin(i * a)));
            cmp.setSize(cmp.getPreferredSize());
            cmp.setFontSize(n_font);
            if (num != null) {
                num.setLocation((int) (x0 + dwi / 2 + r2 * Math.cos(i * a)),
                                (int) (y0 + dhe / 2 + r2 * Math.sin(i * a)));
                num.setSize(num.getPreferredSize());
            }
            for (int j = 0; j < cmp2.length; j++) {
                double a2 = a / cmp2.length;
                cmp2[j].setLocation(
                        (int) (cmp.getX() + r2 * Math.cos(i * a + j * a2)),
                        (int) (cmp.getY() + r2 * Math.sin(i * a + j * a2)));
                cmp2[j].setSize(cmp2[j].getPreferredSize());
                cmp2[j].setFontSize((int) (n_font * 3 / 4));
                GraphPanel.NumberNode num2 = component.getNumberNode(cmp2[j]);
                if (num2 != null) {
                    num2.setLocation(
                            (int) (cmp2[j].getX() +
                                   r3 * Math.cos(i * a + j * a2)),
                            (int) (cmp2[j].getY() +
                                   r3 * Math.sin(i * a + j * a2)));
                    num2.setSize(num.getPreferredSize());
                }
            }
            component.resizeLinks();
        }

        // don't forget the arrows
        for (int i = 0; i < component.getArrows().length; i++) {
            int x1 = component.getFirstNode(i).getLocation().x;
            int y1 = component.getFirstNode(i).getLocation().y;
            int w1 = component.getFirstNode(i).getSize().width;
            int h1 = component.getFirstNode(i).getSize().height;
            int x2 = component.getSecondNode(i).getLocation().x;
            int y2 = component.getSecondNode(i).getLocation().y;
            int w2 = component.getSecondNode(i).getSize().width;
            int h2 = component.getSecondNode(i).getSize().height;
            int x = x1 + w1 / 2 < x2 + w2 / 2 ? x1 + w1 / 2 : x2 + w2 / 2;
            int y = y1 + h1 / 2 < y2 + h2 / 2 ? y1 + h1 / 2 : y2 + h2 / 2;
            int w0 = x1 + w1 / 2 > x2 + w2 / 2 ? x1 + w1 / 2 - x :
                     x2 + w2 / 2 - x;
            int h0 = y1 + h1 / 2 > y2 + h2 / 2 ? y1 + h1 / 2 - y :
                     y2 + h2 / 2 - y;
            component.getArrows()[i].setLocation(x + w0 / 2 - 5,
                                                 y + h0 / 2 - 5);
            component.getArrows()[i].setSize(10, 10);
            component.getArrows()[i].setArrow(
                    LinkFactory.createArrow(x1 + w1 / 2, y1 + h1 / 2,
                                            x2 + w2 / 2, y2 + h2 / 2));
        }
        if (component.getSecondCrown().length == 0) {
            for (int i = 0; i < 2 * component.getFirstCrown().length; i++) {
                quake(component);
            }
        }
    }

    /**
     * Génère une itération du replacement des composants du GraphPanel
     * spécifié.
     * @param component GraphPanel le GraphPanel en question
     * @since 3.0.0
     */
    protected void quake(GraphPanel component) {
        if(System.currentTimeMillis()-start > 3000){
            return; // 3 secondes sont suffisantes pour trouver une bonne place.
        }
        component.getActiveNode().setSize(component.getActiveNode().
                                          getPreferredSize());
        for (int i = 0; i < component.getFirstCrown().length; i++) {
            component.getFirstCrown()[i].setSize(component.getFirstCrown()[i].
                                                 getPreferredSize());
        }
        for (int i = 0; i < component.getSecondCrown().length; i++) {
            component.getSecondCrown()[i].setSize(component.getSecondCrown()[i].
                                                  getPreferredSize());
        }


        /*GraphPanel.Node[] liste = component.getAllNodesSorted();
        for (int i = 0; i < liste.length; i++) {
            for (int j = 0; j < liste.length; j++) {
                int c1 = 0;
                if(component.isInFirstCrown(liste[i])) c1 = 1;
                if(component.isInSecondCrown(liste[i])) c1 = 2;
                int c2 = 0;
                if(component.isInFirstCrown(liste[j])) c2 = 1;
                if(component.isInSecondCrown(liste[j])) c2 = 2;
                Dimension tns = getTranslation(liste[i], liste[j],
                                               component.isLinked(liste[i],liste[j]),
                                               c1, c2);
                trX[i] += tns.width;
                trY[i] += tns.width;
            }
        }*/


        int[] trX0 = new int[1];
        int[] trY0 = new int[1];
        int[] trX1 = new int[component.getFirstCrown().length];
        int[] trY1 = new int[component.getFirstCrown().length];
        int[] trX2 = new int[component.getSecondCrown().length];
        int[] trY2 = new int[component.getSecondCrown().length];
        Dimension d;

        trX0[0] = 0;
        trY0[0] = 0;
        for (int j = 0; j < component.getFirstCrown().length; j++) {
            d = getTranslation(component.getActiveNode(),
                               component.getFirstCrown()[j],
                               component.isLinked(
                                       component.getActiveNode(),
                                       component.getFirstCrown()[j]),
                               0, 1);
            trX1[j] -= d.width;
            trY1[j] -= d.height;
            for(int k=0; k<component.getSecondCrown().length; k++){
                if(component.isInCrownOf(
                        component.getFirstCrown()[j],
                        component.getSecondCrown()[k])){
                    trX2[k] -= d.width;
                    trY2[k] -= d.height;
                }
            }
        }
        for (int j = 0; j < component.getSecondCrown().length; j++) {
            d = getTranslation(component.getActiveNode(),
                               component.getSecondCrown()[j],
                               component.isLinked(
                                       component.getActiveNode(),
                                       component.getSecondCrown()[j]),
                               0, 2);
            trX2[j] -= d.width;
            trY2[j] -= d.height;
        }

        for (int i = 0; i < component.getFirstCrown().length; i++) {
            d = getTranslation(component.getFirstCrown()[i],
                               component.getActiveNode(),
                               component.isLinked(
                                       component.getFirstCrown()[i],
                                       component.getActiveNode())
                               , 1, 0);
            trX1[i] += d.width;
            trY1[i] += d.height;
            for(int k=0; k<component.getSecondCrown().length; k++){
                if(component.isInCrownOf(
                        component.getFirstCrown()[i],
                        component.getSecondCrown()[k])){
                    trX2[k] += d.width;
                    trY2[k] += d.height;
                }
            }
            for (int j = 0; j < component.getFirstCrown().length; j++) {
                d = getTranslation(component.getFirstCrown()[i],
                                   component.getFirstCrown()[j],
                                   component.isLinked(
                                           component.getFirstCrown()[i],
                                           component.getFirstCrown()[j]),
                                   1, 1);
                trX1[i] += d.width;
                trY1[i] += d.height;
                for(int k=0; k<component.getSecondCrown().length; k++){
                    if(component.isInCrownOf(
                            component.getFirstCrown()[i],
                            component.getSecondCrown()[k])){
                        trX2[k] += d.width;
                        trY2[k] += d.height;
                    }
                }
                trX1[j] -= d.width;
                trY1[j] -= d.height;
                for(int k=0; k<component.getSecondCrown().length; k++){
                    if(component.isInCrownOf(
                            component.getFirstCrown()[j],
                            component.getSecondCrown()[k])){
                        trX2[k] -= d.width;
                        trY2[k] -= d.height;
                    }
                }
            }
            for (int j = 0; j < component.getSecondCrown().length; j++) {
                d = getTranslation(component.getFirstCrown()[i],
                                   component.getSecondCrown()[j],
                                   component.isLinked(
                                           component.getFirstCrown()[i],
                                           component.getSecondCrown()[j]),
                                   1, 2);
                trX2[j] -= d.width;
                trY2[j] -= d.height;
            }
        }

        for (int i = 0; i < component.getSecondCrown().length; i++) {
            d = getTranslation(component.getSecondCrown()[i],
                               component.getActiveNode(),
                               component.isLinked(
                                       component.getSecondCrown()[i],
                                       component.getActiveNode()),
                               2, 0);
            trX2[i] += d.width;
            trY2[i] += d.height;
            for (int j = 0; j < component.getFirstCrown().length; j++) {
                d = getTranslation(component.getSecondCrown()[i],
                                   component.getFirstCrown()[j],
                                   component.isLinked(
                                           component.getSecondCrown()[i],
                                           component.getFirstCrown()[j]),
                                   2, 1);
                trX2[i] += d.width;
                trY2[i] += d.height;
            }
            for (int j = 0; j < component.getSecondCrown().length; j++) {
                d = getTranslation(component.getSecondCrown()[i],
                                   component.getSecondCrown()[j],
                                   component.isLinked(
                                           component.getSecondCrown()[i],
                                           component.getSecondCrown()[j]),
                                   2, 2);
                trX2[i] += d.width;
                trY2[i] += d.height;
                trX2[j] -= d.width;
                trY2[j] -= d.height;
            }
        }

        int minX = Integer.MAX_VALUE;
        int minY = Integer.MAX_VALUE;
        component.getActiveNode().setLocation(
                component.getActiveNode().getLocation().x + trX0[0],
                component.getActiveNode().getLocation().y + trY0[0]);
        if (component.getActiveNode().getLocation().x < minX) {
            minX = component.getActiveNode().getLocation().x;
        }
        if (component.getActiveNode().getLocation().y < minY) {
            minY = component.getActiveNode().getLocation().y;
        }
        for (int i = 0; i < trX1.length; i++) {
            component.getFirstCrown()[i].setLocation(
                    component.getFirstCrown()[i].getLocation().x + trX1[i],
                    component.getFirstCrown()[i].getLocation().y + trY1[i]);
            if (component.getFirstCrown()[i].getLocation().x < minX) {
                minX = component.getFirstCrown()[i].getLocation().x;
            }
            if (component.getFirstCrown()[i].getLocation().y < minY) {
                minY = component.getFirstCrown()[i].getLocation().y;
            }
        }
        for (int i = 0; i < trX2.length; i++) {
            component.getSecondCrown()[i].setLocation(
                    component.getSecondCrown()[i].getLocation().x + trX2[i],
                    component.getSecondCrown()[i].getLocation().y + trY2[i]);
            if (component.getSecondCrown()[i].getLocation().x < minX) {
                minX = component.getSecondCrown()[i].getLocation().x;
            }
            if (component.getSecondCrown()[i].getLocation().y < minY) {
                minY = component.getSecondCrown()[i].getLocation().y;
            }
        }
        for (int i = 0; i < component.getNumberNodes().length; i++) {
            if (component.getNumberNodes()[i].getX() < minX) {
                minX = component.getNumberNodes()[i].getX();
            }
            if (component.getNumberNodes()[i].getY() < minY) {
                minY = component.getNumberNodes()[i].getY();
            }
        }
        int xx = (minX < 10) ? ((int) (Math.floor(minX / 10.0))) :
                 ((int) (Math.ceil(minX / 10.0)));
        int yy = (minY < 10) ? ((int) (Math.floor(minY / 10.0))) :
                 ((int) (Math.ceil(minY / 10.0)));
        component.getActiveNode().setLocation(
                component.getActiveNode().getLocation().x - xx,
                component.getActiveNode().getLocation().y - yy);
        for (int i = 0; i < component.getFirstCrown().length; i++) {
            component.getFirstCrown()[i].setLocation(
                    component.getFirstCrown()[i].getLocation().x - xx,
                    component.getFirstCrown()[i].getLocation().y - yy);
        }
        for (int i = 0; i < component.getSecondCrown().length; i++) {
            component.getSecondCrown()[i].setLocation(
                    component.getSecondCrown()[i].getLocation().x - xx,
                    component.getSecondCrown()[i].getLocation().y - yy);
        }
        for (int i = 0; i < component.getNumberNodes().length; i++) {
            component.getNumberNodes()[i].setLocation(
                    component.getNumberNodes()[i].getLocation().x - xx,
                    component.getNumberNodes()[i].getLocation().y - yy);
        }

        for (int i = 0; i < component.getFirstCrown().length; i++) {
            for (int j = 0; j < component.getNumberNodes().length; j++) {
                if (component.getNumberNodes()[j].equals(component.
                        getNumberNode(component.
                                      getFirstCrown()[i]))) {
                    int dx = component.getFirstCrown()[i].getX() +
                             component.getFirstCrown()[i].getWidth() / 2 -
                             component.getActiveNode().getX() -
                             component.getActiveNode().getWidth() / 2;
                    int dy = component.getFirstCrown()[i].getY() +
                             component.getFirstCrown()[i].getHeight() / 2 -
                             component.getActiveNode().getY() -
                             component.getActiveNode().getHeight() / 2;
                    int dw = (component.getNumberNodes()[j].getWidth() -
                              component.getFirstCrown()[i].getWidth()) / 2;
                    int dh = (component.getNumberNodes()[j].getHeight() -
                              component.getFirstCrown()[i].getHeight()) / 2;
                    component.getNumberNodes()[j].setLocation(
                            component.getFirstCrown()[i].getX() + dx / 2 - dw,
                            component.getFirstCrown()[i].getY() + dy / 2 - dh);
                }
            }
        }

        for (int i = 0; i < component.getSecondCrown().length; i++) {
            for (int j = 0; j < component.getNumberNodes().length; j++) {
                if (component.getNumberNodes()[j].equals(component.
                        getNumberNode(component.
                                      getSecondCrown()[i]))) {
                    Component c = null;
                    for (int k = 0; k < component.getFirstCrown().length; k++) {
                        if(component.isInCrownOf(
                                component.getFirstCrown()[k],
                                component.getSecondCrown()[i])){
                            c = component.getFirstCrown()[k];
                        }
                    }if(c != null){
                        int dx = component.getSecondCrown()[i].getX() -
                                 c.getX();
                        int dy = component.getSecondCrown()[i].getY() -
                                 c.getY();
                        component.getNumberNodes()[j].setLocation(
                                component.getSecondCrown()[i].getX() + dx / 2,
                                component.getSecondCrown()[i].getY() + dy / 2);

                    } else {
                        int dx = component.getSecondCrown()[i].getX() -
                                 component.getActiveNode().getX();
                        int dy = component.getSecondCrown()[i].getY() -
                                 component.getActiveNode().getY();
                        component.getNumberNodes()[j].setLocation(
                                component.getSecondCrown()[i].getX() + dx / 2,
                                component.getSecondCrown()[i].getY() + dy / 2);
                    }
                }
            }
        }

        component.resizeLinks();

        // don't forget the arrows
        for (int i = 0; i < component.getArrows().length; i++) {
            int x1 = component.getFirstNode(i).getLocation().x;
            int y1 = component.getFirstNode(i).getLocation().y;
            int w1 = component.getFirstNode(i).getSize().width;
            int h1 = component.getFirstNode(i).getSize().height;
            int x2 = component.getSecondNode(i).getLocation().x;
            int y2 = component.getSecondNode(i).getLocation().y;
            int w2 = component.getSecondNode(i).getSize().width;
            int h2 = component.getSecondNode(i).getSize().height;
            int x = x1 + w1 / 2 < x2 + w2 / 2 ? x1 + w1 / 2 : x2 + w2 / 2;
            int y = y1 + h1 / 2 < y2 + h2 / 2 ? y1 + h1 / 2 : y2 + h2 / 2;
            int w0 = x1 + w1 / 2 > x2 + w2 / 2 ? x1 + w1 / 2 - x :
                     x2 + w2 / 2 - x;
            int h0 = y1 + h1 / 2 > y2 + h2 / 2 ? y1 + h1 / 2 - y :
                     y2 + h2 / 2 - y;
            component.getArrows()[i].setLocation(x + w0 / 2 - 5,
                                                 y + h0 / 2 - 5);
            component.getArrows()[i].setSize(10, 10);
            component.getArrows()[i].setArrow(
                    LinkFactory.createArrow(x1 + w1 / 2, y1 + h1 / 2,
                                            x2 + w2 / 2, y2 + h2 / 2));
        }

    }

    /**
     * Ne génère pas une itération des composants du GraphPanel
     * spécifié.
     * @param component GraphPanel le GraphPanel en question
     * @since 3.5.1
     */
    protected void redraw(GraphPanel component) {
        component.getActiveNode().setSize(component.getActiveNode().
                                          getPreferredSize());
        for (int i = 0; i < component.getFirstCrown().length; i++) {
            component.getFirstCrown()[i].setSize(component.getFirstCrown()[i].
                                                 getPreferredSize());
        }
        for (int i = 0; i < component.getSecondCrown().length; i++) {
            component.getSecondCrown()[i].setSize(component.getSecondCrown()[i].
                                                  getPreferredSize());
        }

        component.getActiveNode().setLocation(
                component.getActiveNode().getLocation().x,
                component.getActiveNode().getLocation().y);
        for (int i = 0; i < component.getFirstCrown().length; i++) {
            component.getFirstCrown()[i].setLocation(
                    component.getFirstCrown()[i].getLocation().x,
                    component.getFirstCrown()[i].getLocation().y);
        }
        for (int i = 0; i < component.getSecondCrown().length; i++) {
            component.getSecondCrown()[i].setLocation(
                    component.getSecondCrown()[i].getLocation().x,
                    component.getSecondCrown()[i].getLocation().y);
        }

        for (int i = 0; i < component.getFirstCrown().length; i++) {
            for (int j = 0; j < component.getNumberNodes().length; j++) {
                if (component.getNumberNodes()[j].equals(component.
                        getNumberNode(component.
                                      getFirstCrown()[i]))) {
                    int dx = component.getFirstCrown()[i].getX() +
                             component.getFirstCrown()[i].getWidth() / 2 -
                             component.getActiveNode().getX() -
                             component.getActiveNode().getWidth() / 2;
                    int dy = component.getFirstCrown()[i].getY() +
                             component.getFirstCrown()[i].getHeight() / 2 -
                             component.getActiveNode().getY() -
                             component.getActiveNode().getHeight() / 2;
                    int dw = (component.getNumberNodes()[j].getWidth() -
                              component.getFirstCrown()[i].getWidth()) / 2;
                    int dh = (component.getNumberNodes()[j].getHeight() -
                              component.getFirstCrown()[i].getHeight()) / 2;
                    component.getNumberNodes()[j].setLocation(
                            component.getFirstCrown()[i].getX() + dx / 2 - dw,
                            component.getFirstCrown()[i].getY() + dy / 2 - dh);
                }
            }
        }

        for (int i = 0; i < component.getSecondCrown().length; i++) {
            for (int j = 0; j < component.getNumberNodes().length; j++) {
                if (component.getNumberNodes()[j].equals(component.
                        getNumberNode(component.
                                      getSecondCrown()[i]))) {
                    Component c = null;
                    for (int k = 0; k < component.getFirstCrown().length; k++) {
                        if(component.isInCrownOf(
                                component.getFirstCrown()[k],
                                component.getSecondCrown()[i])){
                            c = component.getFirstCrown()[k];
                        }
                    }if(c != null){
                        int dx = component.getSecondCrown()[i].getX() -
                                 c.getX();
                        int dy = component.getSecondCrown()[i].getY() -
                                 c.getY();
                        component.getNumberNodes()[j].setLocation(
                                component.getSecondCrown()[i].getX() + dx / 2,
                                component.getSecondCrown()[i].getY() + dy / 2);

                    } else {
                        int dx = component.getSecondCrown()[i].getX() -
                                 component.getActiveNode().getX();
                        int dy = component.getSecondCrown()[i].getY() -
                                 component.getActiveNode().getY();
                        component.getNumberNodes()[j].setLocation(
                                component.getSecondCrown()[i].getX() + dx / 2,
                                component.getSecondCrown()[i].getY() + dy / 2);
                    }
                }
            }
        }

        component.resizeLinks();

        // don't forget the arrows
        for (int i = 0; i < component.getArrows().length; i++) {
            int x1 = component.getFirstNode(i).getLocation().x;
            int y1 = component.getFirstNode(i).getLocation().y;
            int w1 = component.getFirstNode(i).getSize().width;
            int h1 = component.getFirstNode(i).getSize().height;
            int x2 = component.getSecondNode(i).getLocation().x;
            int y2 = component.getSecondNode(i).getLocation().y;
            int w2 = component.getSecondNode(i).getSize().width;
            int h2 = component.getSecondNode(i).getSize().height;
            int x = x1 + w1 / 2 < x2 + w2 / 2 ? x1 + w1 / 2 : x2 + w2 / 2;
            int y = y1 + h1 / 2 < y2 + h2 / 2 ? y1 + h1 / 2 : y2 + h2 / 2;
            int w0 = x1 + w1 / 2 > x2 + w2 / 2 ? x1 + w1 / 2 - x :
                     x2 + w2 / 2 - x;
            int h0 = y1 + h1 / 2 > y2 + h2 / 2 ? y1 + h1 / 2 - y :
                     y2 + h2 / 2 - y;
            component.getArrows()[i].setLocation(x + w0 / 2 - 5,
                                                 y + h0 / 2 - 5);
            component.getArrows()[i].setSize(10, 10);
            component.getArrows()[i].setArrow(
                    LinkFactory.createArrow(x1 + w1 / 2, y1 + h1 / 2,
                                            x2 + w2 / 2, y2 + h2 / 2));
        }
    }


    private Dimension getTranslation(JComponent c1, JComponent c2,
                                     boolean linked,
                                     int crown1, int crown2) {
        int x0 = c1.getX() + c1.getWidth() / 2;
        int x1 = c2.getX() + c2.getWidth() / 2;
        int y0 = c1.getY() + c1.getHeight() / 2;
        int y1 = c2.getY() + c2.getHeight() / 2;
        int w0 = c1.getWidth();
        int h0 = c1.getHeight();
        double dx = Math.max(Math.abs(x1 - x0), w0) - w0;
        double dy = Math.max(Math.abs(y1 - y0), h0) - h0;
        if (dx + dy == 0) {
            if (Math.random() < 0.5) {
                dx++;
            } else {
                dy++;
            } // to avoid division by zero
        }
        int px = (x0 >= x1) ? 1 : -1;
        int py = (y0 >= y1) ? 1 : -1;
        double d = Math.sqrt(dx * dx + dy * dy);
        int tooFar = Integer.MAX_VALUE;
        int tooNear = 0;
        switch (crown1) {
        case 1:
            switch (crown2) {
            case 0:
                tooNear = tooNear1;
                tooFar = tooFar1;
                break;
            case 1:
                tooNear = tooNear1;
                tooFar = tooFar1;
                break;
            };
            break;
        case 2:
            switch (crown2) {
            case 0:
                tooNear = tooNear2;
                tooFar = tooFar2;
                break;
            case 1:
                tooNear = tooNear2;
                tooFar = tooFar2;
                break;
            case 2:
                tooNear = tooNear2;
                tooFar = tooFar2;
                break;
            }
            break;
        }
        double eloignement = Math.max(tooNear - d, 0);
        double rapprochement = 0;
        if (linked) {
            rapprochement = Math.max(d - tooFar, 0);
        }
        int tx = (int) (px * (eloignement / 5 - rapprochement / 10) *
                        Math.abs(dx / (dx + dy)));
        int ty = (int) (py * (eloignement / 5 - rapprochement / 10) *
                        Math.abs(dy / (dx + dy)));
        if (Math.abs(tx) < 5) tx = 0;
        if (Math.abs(ty) < 5) ty = 0;
        return new Dimension(tx, ty);

        /*Dimension repulsion = getRepulsion(c1, c2);
        Dimension attraction = new Dimension(0,0);
        if(linked){
            attraction = getAttraction(c1, c2);
        }
        return new Dimension(repulsion.width + attraction.width,
                             repulsion.height + attraction.height);*/
    }

    /*// constante multiplicative de répulsion
    private final static int K = 10000;
    // charge d'un composant
    private final static int Q = 1;
    // constante de raideur d'un lien
    private final static int S = 1;
    // longueur au repos d'un lien
    private final static int D0 = 50;

    private Dimension getRepulsion(JComponent c1, JComponent c2){
        double distanceX = c2.getX() + c2.getWidth() / 2 -
                           c1.getX() - c1.getWidth() / 2;
        double distanceY = c2.getY() + c2.getHeight() / 2 -
                           c1.getY() - c1.getHeight() / 2;
        if(distanceX==0 && distanceY == 0){
            return new Dimension(0,0);
        }
        double distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
        double force = K * Q * Q / (distance * distance);
        int forceX = (int)(Math.round(force * (distanceX / distance)));
        int forceY = (int)(Math.round(force * (distanceY / distance)));
        System.out.println("Distance : ("+distanceX+" ; "+distanceY+") => "+distance+"  |  Répulsion : "+force+" => ("+forceX+" ; "+forceY+")");
        return new Dimension(forceX, forceY);
    }

    private Dimension getAttraction(JComponent c1, JComponent c2){
        double distanceX = c2.getX() - c1.getX();
        double distanceY = c2.getY() - c1.getY();
        if(distanceX==0 && distanceY == 0){
            return new Dimension(0,0);
        }
        double distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
        double force = -S * Math.max(distance-D0,0);
        int forceX = (int)(Math.round(force * (distanceX / distance)));
        int forceY = (int)(Math.round(force * (distanceY / distance)));
        System.out.println("Distance : ("+distanceX+" ; "+distanceY+") => "+distance+"  |  Attraction : "+force+" => ("+forceX+" ; "+forceY+")");
        return new Dimension(forceX, forceY);
    }*/
}
