#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cell.h"


void ecrire_entete_html(FILE * houtput)
{
    fprintf(houtput, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    fprintf(houtput,
            "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
    fprintf(houtput,
            "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"fr\" lang=\"fr\">\n");
    fprintf(houtput, "  <head>\n");
    fprintf(houtput, "    <title>\n");
    fprintf(houtput, "      &raquo; Debug\n");
    fprintf(houtput, "    </title>\n");
    fprintf(houtput,
            "      <script src=\"jq.js\" type=\"text/javascript\"></script>\n");
    fprintf(houtput,
            "      <script src=\"jq.form.js\" type=\"text/javascript\"></script>\n");
    fprintf(houtput,
            "      <script src=\"jq.cookie.js\" type=\"text/javascript\"></script>\n");
    fprintf(houtput,
            "      <link rel=\"stylesheet\" media=\"all\" type=\"text/css\" title=\"Design\" href=\"css.css\" />\n");
    fprintf(houtput, "\n");
    fprintf(houtput, "<style type=\"text/css\">\n");
    fprintf(houtput, "<!--\n");
    fprintf(houtput, "table {\n");
    fprintf(houtput, "table-layout:fixed;\n");
    fprintf(houtput, "border-style:solid;\n");
    fprintf(houtput, "border-width:thin;\n");
    fprintf(houtput, "font-family: courier, serif;\n");
    fprintf(houtput, "}\n");
    fprintf(houtput, "tr {\n");
    fprintf(houtput, "height: 10px;\n");
    fprintf(houtput, "}\n");
    fprintf(houtput, "td {\n");
    fprintf(houtput, "margin:0;\n");
    fprintf(houtput, "padding:0;\n");
    fprintf(houtput, "border-style:solid;\n");
    fprintf(houtput, "border-color:#ddd;\n");
    fprintf(houtput, "border-width:thin;\n");
    fprintf(houtput, "text-align: center;\n");
    fprintf(houtput, "width: 10px;\n");
    fprintf(houtput, "height: 10px;\n");
    fprintf(houtput, "}\n");
    fprintf(houtput, "td.c1 {\n");
    fprintf(houtput, "background-color:#EEEE00;\n");
    fprintf(houtput, "}\n");
    fprintf(houtput, "td.c2 {\n");
    fprintf(houtput, "background-color:#00EEEE;\n");
    fprintf(houtput, "}\n");
    fprintf(houtput, "-->\n");
    fprintf(houtput, "</style>\n");
    fprintf(houtput, "  </head>\n");
    fprintf(houtput, "  <body>\n");
}

void ecrire_fin_html(FILE * houtput)
{
    fprintf(houtput, "  </body>\n");
    fprintf(houtput, "</html>\n");
}

void genere_tableau_html(FILE * houtput,
                         unsigned long total_cellules,
                         struct cell *tabcell,
                         unsigned long awidth,
                         struct cell *cell_1, struct cell *cell_2)
{
    static unsigned long xxx = 0;
    struct cell *pcell;
    unsigned long i;
    xxx++;
    fprintf(houtput, "<p><a name=\"n%lu\"><table>", xxx);
    for (i = 0; i < total_cellules; i++) {
        pcell = &tabcell[i];

        if ((i % awidth) == 0) {
            fprintf(houtput, "<tr>");
        }
        fprintf(houtput, "<td");
        if (pcell == cell_1) {
            fprintf(houtput, " class=\"c1\"");
        }
        if (pcell == cell_2) {
            fprintf(houtput, " class=\"c2\"");
        }
        fprintf(houtput, ">%lu</td>", pcell->no);
        if ((i % awidth) == (awidth - 1)) {
            fprintf(houtput, "</tr>\n");
        }
    }
    fprintf(houtput, "</table></a>");
    fprintf(houtput, "<a href=\"#n%lu\">Précédent</a>&nbsp;"
            "<a href=\"#n%lu\">Suivant</a>" "</p>", xxx - 1, xxx + 1);
}

/* pour mettre en place le filtre,
   j'aurais pu utiliser les outils d'expressions
   régulières (regcomp, regexec etc.) mais juste
   pour décoder des associations du type :
   aa=bb (x fois) autant le faire "à la main"
*/
int recherche_valeurs(char **param, long *largeur, long *longueur,
                      long *hauteur,
                      char *buffer_erreur, int maxlen_erreur)
{
    char *curr = *param;
    long tmp;
    long largeur_tmp = -1;
    long longueur_tmp = -1;
    long hauteur_tmp = -1;
    int ok;

    while (1) {

        ok = sscanf(curr, "largeur=%ld ", &tmp);
        if (ok == 1) {
            if (largeur_tmp != -1) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "L'option param:largeur est définie deux fois.\n");
                return -1;
            }
            largeur_tmp = tmp;
            while (*curr != ' ') {
                curr++;
                if (!(*curr)) {
                    break;
                }
            }
            if (!(*curr)) {
                break;
            }
            curr++;
            continue;
        }
        ok = sscanf(curr, "longueur=%ld ", &tmp);
        if (ok == 1) {
            if (longueur_tmp != -1) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "L'option param:longueur est définie deux fois.\n");
                return -1;
            }
            longueur_tmp = tmp;
            while (*curr != ' ') {
                curr++;
                if (!(*curr)) {
                    break;
                }
            }
            if (!(*curr)) {
                break;
            }
            curr++;
            continue;
        }

        ok = sscanf(curr, "hauteur=%ld ", &tmp);
        if (ok == 1) {
            if (hauteur_tmp != -1) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "L'option param:hauteur est définie deux fois.\n");
                return -1;
            }
            hauteur_tmp = tmp;
            while (*curr != ' ') {
                curr++;
                if (!(*curr)) {
                    break;
                }
            }
            if (!(*curr)) {
                break;
            }
            curr++;
            continue;
        }
        /* arrivé ici c'est qu'aucun des paramètres n'a été lu
           => break */
        break;
    }

    *param = curr;

    /* arrivé ici c'est qu'on est arrivé au bout de la chaine */
    if ((largeur_tmp < 0) || (longueur_tmp < 0) || (hauteur_tmp < 0)) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Dans les options param:xxx, "
                 "il faut définir la largeur, la longueur et la hauteur.\n");
        if (largeur_tmp < 0) {
            strncat(buffer_erreur,
                    "Vous n'avez pas défini param:largeur=xx.\n",
                    maxlen_erreur);
        }
        if (longueur_tmp < 0) {
            strncat(buffer_erreur,
                    "Vous n'avez pas défini param:longueur=xx.\n",
                    maxlen_erreur);
        }
        if (hauteur_tmp < 0) {
            strncat(buffer_erreur,
                    "Vous n'avez pas défini param:hauteur=xx.\n",
                    maxlen_erreur);
        }
        return -1;
    }
    *largeur = largeur_tmp;
    *longueur = longueur_tmp;
    *hauteur = hauteur_tmp;
    return 0;
}

/* voir long commentaire fonction au dessus
   (!) le paramètre **param doit être valide 
       *et* il est modifié pour aller sur le
       début de la paire aa=bb suivante
*/
int decode_assoc(char **param, unsigned long *index, unsigned long *value)
{
    char *curr = *param;
    int ok;

    ok = sscanf(*param, "%lu=%lu ", index, value);
    if (ok != 2) {
        return -1;
    }

    /* Sauter toute la chaine trouvée :
       (1) d'abord aller jusqu'à '=' :
     */
    while (*curr != '=') {
        curr++;
        if (!(*curr)) {
            return -1;
        }
    }
    curr++;
    /* (2) ensuite tant que 'chiffre', passer :
     */
    while (*curr) {
        if (*curr == ' ') {
            /* faire pointer le pointeur en paramètre
               vers la nouvelle chaine association ('xx=yy') */
            *param = (++curr);
            return 0;
        }
        if ((*curr < '0') || (*curr > '9')) {
            return -1;
        }
        curr++;
    }
    /* faire pointeur en paramètre = NULL signifie :
       "plus aucune association de type 'xx=yy' "
     */
    *param = NULL;
    return 0;
}

struct groupe {
    unsigned int nb_pcells;
    cell **tab_pcells;
};

int mettre_cellule_dans_nouveau_groupe(struct groupe **groupes,
                                       unsigned long *total_groupes,
                                       struct cell *pcell,
                                       char *buffer_erreur,
                                       int maxlen_erreur)
{
    struct groupe *groupes_tmp = *groupes;
    struct cell **tab_pcells_tmp;
    groupes_tmp =
        (struct groupe *) realloc(groupes_tmp,
                                  (sizeof(struct groupe)) *
                                  ((*total_groupes) + 1));
    if (groupes_tmp == NULL) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur d'allocation : manque de mémoire.\n");
        return -1;
    }
    /* ré-allocation réussie => assigner */
    (*groupes) = groupes_tmp;
    /* à ce nouveau tableau, allouer la place pour
       le premier pointeur cell */
    groupes_tmp[*total_groupes].nb_pcells = 1;
    tab_pcells_tmp = (struct cell **) malloc(sizeof(struct cell *));
    if (tab_pcells_tmp == NULL) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur d'allocation : manque de mémoire.\n");
        return -1;
    }
    /* allocation réussie => assigner */
    groupes_tmp[*total_groupes].tab_pcells = tab_pcells_tmp;
    /* premier pointeur cell = cell en cours */
    groupes_tmp[*total_groupes].tab_pcells[0] = pcell;
    (*total_groupes)++;
    pcell->tag = (*total_groupes);
    return 0;
}

int mettre_cellule_dans_groupe_existant(struct groupe *groupes,
                                        int index,
                                        int index_lien_a_lier,
                                        struct cell *pcell,
                                        char *buffer_erreur,
                                        int maxlen_erreur)
{
    struct cell **tab_pcells_tmp;
    int i, nb;

    /* (!) index = tag = (index + 1) du tableau => faire -1 */
    i = index - 1;
    /* si le lien pointe sur une case déja faite :
       tag cellule en cours = tag cellule dest */
    pcell->tag = index;

    /* allouer l'espace pour ajouter la cellule */
    nb = (groupes[i].nb_pcells) + 1;
    tab_pcells_tmp =
        (struct cell **) realloc(groupes[i].tab_pcells,
                                 sizeof(struct cell *) * nb);
    if (tab_pcells_tmp == NULL) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur d'allocation : manque de mémoire.l\n");
        return -1;
    }
    /* ré-allocation réussie => assigner */
    groupes[i].tab_pcells = tab_pcells_tmp;
    groupes[i].nb_pcells = nb;
    /* mettre le noeud dans la liste */
    groupes[i].tab_pcells[nb - 1] = pcell;
    /* creuser un passage entre les deux cellules : */
    if (cell_carre_deplacer_link_vers_paths(pcell, index_lien_a_lier)) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur de recherche de cellule, pas normal\n");
        return -1;
    }

    return 0;
}

int plugin_algo_carre_pondere(unsigned long total_cellules,
                              unsigned long total_liens,
                              cell * tabcell,
                              char *params,
                              unsigned long nb_option_force_link,
                              struct cell_option_force_link
                              *tab_option_force_link, char *buffer_erreur,
                              int maxlen_erreur)
{
    /* retour = ok par défaut : */
    int ret = 0;

    /* les variables 'XXX_tmp' sont utilisées pour les realloc */
    struct cell **tab_todo;
    struct cell **tab_todo_tmp;
    struct groupe *groupes;
    struct cell **tab_pcells_tmp;

    struct cell *pcell;
    struct cell *plink;
    unsigned long i, j, k, m, n, q;
    unsigned long total_groupes;
    unsigned long nb_cells_to_do;
    unsigned char fusion;
    unsigned char boucle_a_faire;

    /* pour le tirage forcé des cellules
       qui ont un attribut "no" != 0
     */
    unsigned long no_to_do = 0;
    unsigned long no_precedent = 0;
    /*int ecart; */
    struct cell *cell_a_reprendre;
    struct cell *cell_ok;
    struct cell_option_force_link
    *ptab_force;

    /* debug
     */
    FILE *houtput;

    /* le poids des tags des liens (pour influencer
       le tirage aléatoire) :
     */
#define MAX_POIDS (unsigned long)50
    unsigned long nb_poids = 0;
    unsigned long poids[MAX_POIDS];
    unsigned long tire[MAX_POIDS];
    char *paramtmp = params;

    /* pour le calcul du rand() : */
    unsigned long poids_rand_max;
    long poids_rand;

    long largeur = -1;
    long longueur = -1;
    long hauteur = -1;

    if (!params) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Il faut obligatoirement des paramètres de poids pour l'algorithme pondéré.\n");
        return -1;
    }
    if (total_liens < 1) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Le nombre de liens < 1. Impossible de créer quoi que ce soit.\n");
        return -1;
    }
    ret =
        recherche_valeurs(&paramtmp, &largeur, &longueur, &hauteur,
                          buffer_erreur, maxlen_erreur);
    if (!paramtmp) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Vous avez défini la largeur, la longueur et la hauteur mais c'est insuffisant.\n"
                 "Il faut obligatoirement des paramètres de poids pour l'algorithme pondéré.\n");
        return -1;
    }
    while ((ret = decode_assoc(&paramtmp, &i, &poids[nb_poids])) == 0) {
        /* ok on a ajouté un param => tout de suite incrémenter : */
        nb_poids++;
        if (paramtmp == NULL)
            break;
        if (i != nb_poids) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur : les associations "
                     "en paramètre de type 'xx=yy' doivent "
                     "se suivre de manière incrémentale "
                     "en partant de 1.\n"
                     "Exemple : 1=25 2=50 3=12 4=10\n"
                     "Ce n'est pas le cas, le 'xx' de 'xx=yy' "
                     "qui pose problème est le numéro %lu.\n"
                     "Il a été trouvé en position %lu donc 'xx' aurait dû "
                     "avoir la valeur %lu.\n"
                     "(Il aurait fallu lire quelque "
                     "chose du genre '%lu=yy').\n",
                     i, nb_poids, nb_poids, nb_poids);
            ret = -1;
        }
        if (nb_poids > MAX_POIDS) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur : le nombre max. "
                     "d'associations de type 'xx=yy'"
                     "en paramètres est dépassé.\n"
                     "Le maximum d'associations est %lu.\n", MAX_POIDS);
            ret = -1;
        }
    }
    if (ret) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur : les paramètres doivent "
                 "être des associations explicitant le poids "
                 "des tags des liens du labyrinthe.\n"
                 "Ce n'est pas le cas.\n"
                 "Elle doivent être du type 'xx=yy' et "
                 "se suivre de manière incrémentale "
                 "en partant de 1.\n"
                 "Exemple : 1=25 2=50 3=12 4=10\n"
                 "Cela n'est pas le cas.\n");
        ret = -1;
    }
    /* ok le poids des tags des liens est chargé */
    /* on vérifie que tous les liens ont un tag valide 
       (tag valide = un tag qui est dans le tableau des poids) */
    for (i = 0; i < total_cellules; i++) {
        pcell = &tabcell[i];
        for (j = 0; j < pcell->nb_links; j++) {
            k = pcell->links[j].tag;
            if ((k < 1) || (k > nb_poids)) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "Il y a %lu associations de type 'xx=yy' "
                         "déclarées en paramètres pour les poids des liens.\n"
                         "La valeur pour chaque lien doit être comprise entre 1 et %lu.\n"
                         "Pourtant la valeur pour le lien %lu de la cellule %lu "
                         "vaut %lu.\n"
                         "Cette valeur n'est pas valide.\n",
                         nb_poids, nb_poids, j + 1, i + 1, k);
                return -1;
            }
        }
    }
    /* ok maintenant on force les path et les walls */
    ptab_force = tab_option_force_link;
    for (i = 0; i < nb_option_force_link; i++) {
        pcell = &tabcell[ptab_force->id_cell - 1];
        k = ptab_force->id_link - 1;
        j = 0;
        while (j < pcell->nb_links) {
            if (pcell->links[j].link->no == k) {
                break;
            }
            j++;
        }
        if (j == pcell->nb_links) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Un paramètre %s est problématique :\n"
                     "Ce paramètre est %lu=%lu\n"
                     "pourtant parmi tous les liens de la cellule %lu,\n"
                     "aucun ne pointe vers la cellule %lu.\n",
                     (ptab_force->
                      path_ou_wall ? "force_walls" : "force_paths"),
                     ptab_force->id_cell, ptab_force->id_link,
                     ptab_force->id_cell, ptab_force->id_link);
            return -1;

        }
        if (ptab_force->path_ou_wall) {
            cell_deplacer_link_vers_walls(pcell, j);
        } else {
            cell_deplacer_link_vers_paths(pcell, j);
        }
        ptab_force++;
    }

    /* groupes est composé ainsi :

       groupe 1 | groupe 2 | groupe 3 | groupe 4 | ...

       (!) NB : tag cellule = id de son groupe

     */
    tab_todo = NULL;
    groupes = NULL;
    total_groupes = 0;
    bzero(tire, sizeof(tire));

    /* construire une liste avec toutes les cellules du laby :
     */
    tab_todo =
        (struct cell **) calloc(total_cellules, sizeof(struct cell *));

    if (tab_todo == NULL) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur d'allocation : manque de mémoire.\n");
        return -1;
    }
    for (i = 0; i < total_cellules; i++) {
        tab_todo[i] = &(tabcell[i]);
        /* "tag" a été lu à partir du fichier,
           => il est valide
           => pourtant il va être mis à 0 (il sert
           à déterminer le groupe auquel
           appartient la cellule)
           => le copier dans "no"
           => dans "no" on aura les cellules à
           traiter en priorité
         */
        tabcell[i].no = tabcell[i].tag;
        if (tabcell[i].no) {
            no_to_do += tabcell[i].no;
        }
        tabcell[i].tag = 0;
    }

    /* après avoir appliqué les options
       "forcer_walls" et "forcer_paths",
       des cellules peuvent ne plus avoir
       de lien => il faut les ranger
       tout de suite dans des groupes
       uniques */
    for (i = 0; i < total_cellules; i++) {
        pcell = &(tabcell[i]);
        if (pcell->nb_links == 0) {
            if (pcell->nb_paths == 0) {
                /* aucun lien *et* aucun 
                   chemin => la cellule est 
                   isolée => erreur */
                snprintf(buffer_erreur, maxlen_erreur,
                         "Erreur avec les directives "
                         "'force_walls' et 'force_paths'.\n"
                         "Après les avoir appliquées, la cellule no.%lu "
                         "n'a plus de link ni de paths\n"
                         "=> Elle est isolée, c'est anormal.\n", i + 1);
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
            /* verifier que parmi les cellules
               qui sont dans les paths, au moins
               une a un link ou un path vers une
               autre cellule */
            j = 0;
            while (j < pcell->nb_paths) {
                /* si une cellule reliée a au moins
                   un link c'est ok : */
                if (pcell->paths[j].link->nb_links > 0) {
                    break;
                }
                /* si une cellule reliée a plus qu'un path
                   ca signifie qu'elle est reliée aussi
                   à une autre cellule => c'est ok : */
                if (pcell->paths[j].link->nb_paths > 1) {
                    break;
                }
                j++;
            }
            if (j == pcell->nb_paths) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "Erreur avec les directives "
                         "'force_walls' et 'force_paths'.\n"
                         "Après les avoir appliquées, la cellule no.%lu "
                         "est n'est liée avec aucune cellule qui donne\n"
                         "la possibilité d'être liée avec les autres\n"
                         "=> Elle est dans un groupe isolé, c'est anormal.\n",
                         i + 1);
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
            if (mettre_cellule_dans_nouveau_groupe
                (&groupes, &total_groupes, pcell, buffer_erreur,
                 maxlen_erreur)) {
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
            nb_cells_to_do--;
        }
    }
/*
    for (i = 0; i < total_cellules; i++) {
        cell_print(&(tabcell[i]));
    }
*/
    houtput = fopen("./tmp.html", "wb+");
    if (houtput == NULL) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Erreur d'ouverture en écriture "
                 "du fichier <./tmp.html>.\n");
        ret = -1;
        goto plugin_algo_carre_arbre_fin;
    }
    ecrire_entete_html(houtput);
    /* boucle principale
     */
    nb_cells_to_do = total_cellules;
    cell_a_reprendre = NULL;
    while (nb_cells_to_do) {
        i = rand() % nb_cells_to_do;
        pcell = tab_todo[i];
        if (pcell->tag) {
            /* il est déjà dans un groupe (mis dans un 
               groupe lors de l'initialisation
               (voir au dessus)) 
               => cellule courante ne
               sert plus à rien dans tab_todo
               => l'écraser par la dernière cellule */
            tab_todo[i] = tab_todo[nb_cells_to_do - 1];
            nb_cells_to_do--;
            continue;
        }
        /* tirer un lien au hasard en se servant des poids   
         */
        /* calcul du poids total */
        poids_rand_max = 0;
        for (j = 0; j < pcell->nb_links; j++) {
            poids_rand_max += poids[pcell->links[j].tag - 1];
        }
        if (!poids_rand_max) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur 3 forcer tirage : "
                     "poids_rand_max = 0 " "pour cellule %lu\n", i);
            ret = -1;
            goto plugin_algo_carre_arbre_fin;

        }
/*        poids_rand = (int) (poids_rand_max * (rand() / RAND_MAX));
*/
        poids_rand = rand() % poids_rand_max;
        m = poids_rand;
        j = 0;
        while (1) {
            k = poids[pcell->links[j].tag - 1];
            poids_rand -= k;
            if (poids_rand < 0) {
                break;
            }
            j++;
        }
        tire[pcell->links[j].tag - 1]++;
        plink = pcell->links[j].link;
        k = plink->tag;
        if (k == 0) {
            /* dest pas déjà dans un groupe
             */
            /* si le lien pointe sur une case PAS faite,
               créer nouveau groupe */
            if (mettre_cellule_dans_nouveau_groupe
                (&groupes, &total_groupes, pcell, buffer_erreur,
                 maxlen_erreur)) {
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
            /*
               => RAF ! cellule dest du lien
               penser à la traiter :
               essayer de l'ajouter immédiatement
               et de la lier
             */
        } else {
            if (mettre_cellule_dans_groupe_existant
                (groupes, k, j, pcell, buffer_erreur, maxlen_erreur)) {
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
        }
        /* cellule courante est sauvée => elle ne 
           sert plus à rien dans tab_todo
           => l'écraser par la dernière cellule */
        tab_todo[i] = tab_todo[nb_cells_to_do - 1];
        nb_cells_to_do--;
        /* libérer de la mémoire par paquets */
        if (nb_cells_to_do > 20000) {
            if ((nb_cells_to_do % 20000) == 0) {
                tab_todo_tmp =
                    (struct cell **) realloc(tab_todo,
                                             nb_cells_to_do *
                                             sizeof(struct cell *));
                if (tab_todo_tmp == NULL) {
                    snprintf(buffer_erreur, maxlen_erreur,
                             "Erreur d'allocation : manque de mémoire.\n");
                    ret = -1;
                    goto plugin_algo_carre_arbre_fin;
                }
                /* ré-allocation réussie => assigner */
                tab_todo = tab_todo_tmp;
            }
        }
    }

    for (j = 0; j < nb_poids; j++) {
        printf("%lu (%lu) =%lu", j, poids[j], tire[j]);
        if (j < (nb_poids - 1)) {
            printf("%c", 9);
        }
    }

    printf("\n");
    /*
       maintenant, toutes les cases sont "groupées" dans
       des groupes (= tableaux) de cases 
       (dans chaque groupe, toutes les
       cases sont reliées entre elles par un lien).
       => il faut fusionner les groupes
       => chercher des cellules qui se touchent entre deux 
       tableaux.
       => il faut parcourir chaque tableau et faire :
       - pour chaque case regarder sur les liens restants
       - si l'un des liens pointe vers une cellule présente
       dans un autre tableau, alors fusionner les tableaux
       - sinon passer à la cellule suivante 
     */
    /* boucle sur les groupes de tableaux : 
       tant qu'il y a plus de un tableau */
    i = 0;
    boucle_a_faire = 0;
    while (i < total_groupes) {
        j = 0;
        fusion = 0;
        /* boucle sur le groupe courant : */
        while (j < groupes[i].nb_pcells) {
            pcell = groupes[i].tab_pcells[j];
            m = pcell->tag;
            /* just pour vérif, on devrait toujours avoir
               tag = (index + 1) : */
            if (m != (i + 1)) {
                snprintf(buffer_erreur, maxlen_erreur,
                         "Erreur : (m != (i+1)) => (%lu != (%lu))\n",
                         m, i + 1);
                ret = -1;
                goto plugin_algo_carre_arbre_fin;
            }
            /* pour chaque cell, parcourir ses liens : */
            for (k = 0; k < pcell->nb_links; k++) {
                plink = pcell->links[k].link;
                n = plink->tag;
                if (n != m) {
                    /* le lien est dans un autre "groupe"
                       => fusionner ces deux groupes :
                     */
                    /* (!) m = tag = (index + 1) du tableau => faire -1 */
                    n--;
                    /* assigner à tout le groupe 2 le tag du groupe 1 */
                    for (q = 0; q < groupes[n].nb_pcells; q++) {
                        groupes[n].tab_pcells[q]->tag = m;
                    }
                    /* allocation tableau tab tmp = 
                       taille groupe 1 + taille groupe 2 */
                    q = groupes[i].nb_pcells + groupes[n].nb_pcells;
                    tab_pcells_tmp = (struct cell **)
                        malloc(q * sizeof(struct cell *));
                    if (tab_pcells_tmp == NULL) {
                        snprintf(buffer_erreur, maxlen_erreur,
                                 "Erreur d'allocation : manque de mémoire.l\n");
                        ret = -1;
                        goto plugin_algo_carre_arbre_fin;
                    }
                    /* copie du groupe 1 dans tab tmp */
                    memcpy(tab_pcells_tmp, groupes[i].tab_pcells,
                           groupes[i].nb_pcells * sizeof(struct cell *));
                    /* libérer le groupe 1 et y mettre tab tmp */
                    free(groupes[i].tab_pcells);
                    /* assigner groupe 1 = tab tmp */
                    groupes[i].tab_pcells = tab_pcells_tmp;
                    /* déplacer tab tmp pour copie groupe 2 */
                    tab_pcells_tmp += groupes[i].nb_pcells;
                    /* tab tmp est à la fin du groupe 1 
                       => copie du groupe 2 */
                    memcpy(tab_pcells_tmp, groupes[n].tab_pcells,
                           groupes[n].nb_pcells * sizeof(struct cell *));
                    /* nb de cellules = nb groupe 1 + nb groupe 2 */
                    groupes[i].nb_pcells = q;
                    /* libérer le groupe 2 */
                    free(groupes[n].tab_pcells);
                    groupes[n].tab_pcells = NULL;
                    groupes[n].nb_pcells = 0;
                    /* enfin, faire une ouverture sur les deux cellules */
                    /* rechercher le lien opposé de l'autre côté */
                    for (q = 0; q < plink->nb_links; q++) {
                        if (plink->links[q].link == pcell)
                            break;
                    }
                    if (q == plink->nb_links) {
                        snprintf(buffer_erreur, maxlen_erreur,
                                 "Erreur de recherche de cellule, pas normal\n");
                        ret = -1;
                        goto plugin_algo_carre_arbre_fin;
                    }
                    cell_deplacer_link_vers_paths(pcell, k);
                    cell_deplacer_link_vers_paths(plink, q);
                    /* fusion = 1 signie : des tableaux
                       ont été fusionnés, refaire une passe */
                    fusion = 1;
                    i = n;
                    boucle_a_faire = 1;
                    break;
                }
            }
            if (fusion) {
                break;
            }
            j++;
        }
        if (!fusion) {
            /* => arrivé à la fin du groupe
               sans trouver de cellule ayant
               un lien vers un autre groupe */
            /* => groupe suivant */
            i++;
            if (i == total_groupes) {
                if (boucle_a_faire) {
                    /* on ne refait qu'une passe */
                    boucle_a_faire = 0;
                    i = 0;
                }
            }
        }
    }

  plugin_algo_carre_arbre_fin:

    if (houtput != NULL) {
        ecrire_fin_html(houtput);
        fclose(houtput);
    }
    /* libération de toutes les ressources */
    for (i = 0; i < total_groupes; i++) {
        free(groupes[i].tab_pcells);
    }

    free(groupes);
    free(tab_todo);
    if (ret == 0) {
        /* tout s'est bien passé correctement 
           => on a fait toutes les ouvertures
           => tous les liens qui restent sont
           des murs :
         */
        for (i = 0; i < total_cellules; i++) {
            pcell = &(tabcell[i]);
            while (pcell->nb_links) {
                cell_deplacer_link_vers_walls(pcell, 0);
            }
        }
        /* (!) on s'est servi de l'attribut "no"
           pour forcer certain tirages, mais il 
           faut remettre no à sa valeur d'origine
           = index de la cellule :
         */
        for (i = 0; i < total_cellules; i++) {
            tabcell[i].no = i;
        }
        /* (!) arrivé ici, les tag des cellules
           ne sont plus du tout valides
         */
    }
    return ret;
}
