#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <strings.h>
#include <time.h>

#include "cell.h"
#include "opts.h"
#include "utils.h"
#include "verif.h"
#include "verif_liens_bidirectionnels.h"
#include "lecture.h"
#include "ecriture.h"

#include "plugin_algo_groupes.h"

/* pour pouvoir éventuellement être entièrement silencieux : */
#define OUT( x ) if (opt_silent_mode==0) { printf( x ); fflush(NULL); }

int main(int argc, char *argv[])
{
    /* variables du labyrinthe */
    unsigned char id_algo = 0;
    unsigned long total_cellules = 0;
    unsigned long total_liens = 0;
    char params[500];
    unsigned long begin_total_cells;
    unsigned long begin_nb_cells_min;
    unsigned long begin_nb_cells_max;
    unsigned long end_total_cells;
    unsigned long end_nb_cells_min;
    unsigned long end_nb_cells_max;
    unsigned long nb_options_force_link;
    struct cell_option_force_link *tab_option_force_link = NULL;

    unsigned long i;
    int ret;
    char buffer_erreur[500] = "";
    char buffer_tmp[500] = "";

    struct cell *tabcell;
    struct cell_link *tablinks;

    /* options du programme */
    char nom_fichier_entree[500] = "";
    char nom_fichier_sortie[500] = "";
    /* défaut = pas afficher aide */
    unsigned char help = 0;
    /* défaut = afficher tout     */
    unsigned char opt_silent_mode = 0;

    if (valide_opts(argc,
                    argv, &help, &opt_silent_mode,
                    nom_fichier_entree,
                    sizeof(nom_fichier_entree) - 1,
                    nom_fichier_sortie,
                    sizeof(nom_fichier_sortie) - 1,
                    buffer_erreur, sizeof(buffer_erreur) - 1)) {
        /* buffer erreur entièrement initialisé */
        OUT(buffer_erreur);
        exit(EXIT_FAILURE);
    }
    if (help) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1, "Aide demandée :\n");
        STRCAT_STR_USAGE(buffer_tmp, sizeof(buffer_tmp) - 1);
        OUT(buffer_tmp);
        exit(EXIT_SUCCESS);
    }

    sprintf_time_desc(buffer_tmp, sizeof(buffer_tmp) - 1,
                      "-   Début du programme");
    OUT(buffer_tmp);
    snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
             "    Fichier en entrée : <%s>\n", nom_fichier_entree);
    OUT(buffer_tmp);
    snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
             "    Fichier en sortie : <%s>\n", nom_fichier_sortie);
    OUT(buffer_tmp);

    /* vérification de la cohérence du fichier */
    OUT("(1) Vérification de la cohérence du fichier...\n");
    bzero(params, sizeof(params));
    ret = verif_coherence_fichier(nom_fichier_entree,
                                  &id_algo,
                                  &total_cellules,
                                  &total_liens,
                                  params,
                                  sizeof(params),
                                  &begin_total_cells,
                                  &begin_nb_cells_min,
                                  &begin_nb_cells_max,
                                  &end_total_cells,
                                  &end_nb_cells_min,
                                  &end_nb_cells_max,
                                  &nb_options_force_link,
                                  buffer_erreur, sizeof(buffer_erreur));
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de vérification de cohérence\n%s",
                 buffer_erreur);
        OUT(buffer_tmp);
        exit(EXIT_FAILURE);
    }

    OUT("    Aucune incohérence détectée.\n");

    OUT("(2) Allocations mémoire...\n");
    OUT("    Nombre de cellules       : ");
    snprintf_format_monetaire(buffer_tmp, sizeof(buffer_tmp) - 1,
                              total_cellules);
    OUT(buffer_tmp);
    OUT("\n    Nombre de liens          : ");
    snprintf_format_monetaire(buffer_tmp, sizeof(buffer_tmp) - 1,
                              total_liens);
    OUT(buffer_tmp);
    OUT("\n    Nombre de liens à forcer : ");
    snprintf_format_monetaire(buffer_tmp, sizeof(buffer_tmp) - 1,
                              nb_options_force_link);
    OUT(buffer_tmp);
    OUT("\n");
    tabcell = calloc(total_cellules, sizeof(cell));
    if (tabcell == NULL) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur d'allocation des cellules "
                 "(%lu cellules à allouer).\n", total_cellules);
        OUT(buffer_tmp);
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < total_cellules; i++) {
        tabcell[i].no = i;
    }

    tablinks = calloc(total_liens, sizeof(cell_link));
    if (tablinks == NULL) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur d'allocation des liens "
                 "(%lu liens à allouer).\n", total_liens);
        OUT(buffer_tmp);
        free(tabcell);
        exit(EXIT_FAILURE);
    }
    tab_option_force_link =
        calloc(nb_options_force_link, sizeof(cell_option_force_link));
    if (tab_option_force_link == NULL) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur d'allocation des options pour forcer "
                 "les liens "
                 "(%lu liens à allouer).\n", nb_options_force_link);
        OUT(buffer_tmp);
        free(tabcell);
        free(tablinks);
        exit(EXIT_FAILURE);
    }

    OUT("    Allocations réussies.\n");

    /* mise en mémoire du laby */
    OUT("(3) Lecture du fichier...\n");
    /* 0 = lire tout (1 = ne lire que tags cellules) */
    ret = lecture(nom_fichier_entree,
                  /* id_algo, */ tabcell, tablinks,
                  tab_option_force_link, 0,
                  buffer_erreur, sizeof(buffer_erreur));
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de lecture du fichier\n%s", buffer_erreur);
        OUT(buffer_tmp);
        free(tabcell);
        free(tablinks);
        free(tab_option_force_link);
        exit(EXIT_FAILURE);
    }
    OUT("    Lecture réussie.\n");

    OUT("(4) Vérif liens carrés corrects...\n");
    ret =
        verif_liens_bidirectionnels(total_cellules,
                                    tabcell,
                                    buffer_erreur, sizeof(buffer_erreur));
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de calcul :\n%s", buffer_tmp);
        OUT(buffer_tmp);
        free(tabcell);
        free(tablinks);
        free(tab_option_force_link);
        exit(EXIT_FAILURE);
    }

    /* calcul du laby */
    srand(time(NULL) * getpid());
    OUT("(5) Calcul en cours...\n");
    snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
             "    Params = <%s>\n", params);
    OUT(buffer_tmp);

    ret = plugin_algo_groupes(total_cellules,
                              total_liens,
                              tabcell,
                              params, nb_options_force_link,
                              tab_option_force_link,
                              buffer_erreur, sizeof(buffer_erreur));
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de calcul :\n%s", buffer_erreur);
        OUT(buffer_tmp);
        free(tabcell);
        free(tablinks);
        free(tab_option_force_link);
        exit(EXIT_FAILURE);
    }
    /*
       plugin_algo_carre_riviere(total_cellules,
       total_liens,
       tabcell,
       NULL, buffer_erreur, sizeof(buffer_erreur));
     */
    OUT("    Calcul terminé.\n");

    OUT("(6) Re-lecture pour remettre les tags...\n");
    /* (!) ne lire que tags et n.s cellules : */
    ret = lecture(nom_fichier_entree,
                  tabcell, tablinks,
                  tab_option_force_link, 1,
                  buffer_erreur, sizeof(buffer_erreur));
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de lecture des tags du fichier\n%s",
                 buffer_erreur);
        OUT(buffer_tmp);
        free(tabcell);
        free(tablinks);
        free(tab_option_force_link);
        exit(EXIT_FAILURE);
    }
    OUT("    Ok.\n");
    /*
       for (i = 0; i < total_cellules; i++) {
       cell_print(&(tabcell[i]));
       }
       print_laby(tabcell, total_cellules, 7, 7, 1);
     */
    /* génération du fichier en sortie */
    OUT("(7) Génération du fichier sortie...\n");
    if (genere_fichier(nom_fichier_sortie, params,
                       total_cellules, total_liens, tabcell,
                       buffer_erreur, sizeof(buffer_erreur))) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur d'écriture dans le fichier de sortie <%s>.\n%s",
                 nom_fichier_sortie, buffer_erreur);
        OUT(buffer_tmp);
    }
    OUT("    Génération terminée.\n");

    /* libération des ressources allouées */
    free(tabcell);
    free(tablinks);
    free(tab_option_force_link);

    sprintf_time_desc(buffer_tmp, sizeof(buffer_tmp) - 1,
                      "-   Fin du programme");
    OUT(buffer_tmp);

    exit(EXIT_SUCCESS);
}
