#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>

#include "output_autohotkey.h"
#include "opts.h"
#include "utils.h"
#include "cell.h"
#include "tm.h"
#include "laby_lecture.h"

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

/* nb max d'objets différents possibles dans le tableau qui énumère
   les objets présents */
#define MAX_OBJETS 300

unsigned long total_cells = 0;
unsigned long total_links = 0;
unsigned long largeur = 0;
unsigned long longueur = 0;
unsigned long hauteur = 0;
unsigned char rewrite_fichier_sortie = 1;

/* fonction appelée lors de la vérification de la validité
   du fichier en entrée.
   fonction qui intègre les paramètres éventuels
   du fichier en entrée */
int analyse_params(char *param_lu, char *buffer_erreur, int maxlen_erreur)
{
    int ok;
    unsigned long i;

    /* déclaration de la largeur en tant que paramètre */
    ok = sscanf(param_lu, "params:largeur=%lu", &i);
    if (ok == 1) {
        if (largeur) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur : deux fois déclaration de la largeur.\n");
            return -1;
        }
        largeur = i;
    }

    /* déclaration de la longueur en tant que paramètre */
    ok = sscanf(param_lu, "params:longueur=%lu", &i);
    if (ok == 1) {
        if (longueur) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur : deux fois déclaration "
                     "de la longueur.\n");
            return -1;
        }
        longueur = i;
    }

    /* déclaration de la hauteur en tant que paramètre */
    ok = sscanf(param_lu, "params:hauteur=%lu", &i);
    if (ok == 1) {
        if (hauteur) {
            snprintf(buffer_erreur, maxlen_erreur,
                     "Erreur : deux fois déclaration "
                     "de la hauteur.\n");
            return -1;
        }
        hauteur = i;
    }

    return 0;
}


/* fonction appelée lors de la vérification de la validité
   du fichier en entrée.
   fonction de vérification que les paramètres sont ok */
int verif_params_ok(char *buffer_erreur, int maxlen_erreur)
{
    if (largeur == 0) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Déclaration de cellule trop tôt :\n"
                 "La largeur n'est pas encore déclarée.\n");
        return -1;
    }
    if (longueur == 0) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Déclaration de cellule trop tôt :\n"
                 "La longueur n'est pas encore déclarée.\n");
        return -1;
    }
    if (hauteur == 0) {
        snprintf(buffer_erreur, maxlen_erreur,
                 "Déclaration de cellule trop tôt :\n"
                 "La hauteur n'est pas encore déclarée.\n");
        return -1;
    }

    return 0;
}

void assigne_si_vide(struct cell_tm *c_tm, int id_objet)
{
    if (c_tm->objet == OBJET_VIDE) {
        c_tm->objet = id_objet;
    }
}

int main(int argc, char *argv[])
{
    /* (!) gros buffer erreur, les messages sont détaillés : */
    long i, j;
    char buffer_erreur[1000];
    char buffer_tmp[1000];

    /* 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;

    int ret;
    struct cell *tabcells;
    struct cell_link *tablinks;

    /* tableau Trackmania */
    struct cell_tm *tab_tm;

    /* pour les optimisations */
    long tm_taille_max_x = TM_TAILLE_MAX_X;
    long tm_taille_max_y = TM_TAILLE_MAX_Y;
    long tm_taille_max_z = TM_TAILLE_MAX_Z;

    /* tableau qui recense tous les différents
       objets de la map TM : */
    long tab_objets[MAX_OBJETS];
    long nb_objets;

    /*------------------------------------*/
    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, &rewrite_fichier_sortie,
         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);

    OUT("    Génération des macros " "Autohotkey pour Trackmania...\n");
    OUT("(0) - Fichier en entrée :\n");
    OUT("      ");
    OUT(nom_fichier_entree);
    OUT("\n");
    OUT("    - Fichier en sortie :\n");
    OUT("      ");
    OUT(nom_fichier_sortie);
    OUT("\n");
    if (rewrite_fichier_sortie) {
        OUT("      Mode création\n");
    } else {
        OUT("      Mode ajout au fichier existant\n");
    }
    OUT("(1) Vérification de cohérence du fichier...\n");
    ret = laby_verif_fichier(nom_fichier_entree,
                             &total_cells,
                             &total_links,
                             &analyse_params,
                             &verif_params_ok,
                             buffer_erreur, sizeof(buffer_erreur) - 1);
    if (ret) {
        snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
                 "Erreur de vérification "
                 "du fichier labyrinthe :\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_cells);
    OUT(buffer_tmp);
    OUT("\n    Nombre de liens          : ");
    snprintf_format_monetaire(buffer_tmp, sizeof(buffer_tmp) - 1,
                              total_links);
    OUT(buffer_tmp);
    OUT("\n");
    tabcells = calloc(total_cells, sizeof(cell));
    if (tabcells == NULL) {
        snprintf(buffer_erreur, sizeof(buffer_erreur) - 1,
                 "Erreur d'allocation des cellules "
                 "(%lu cellules à allouer).\n", total_cells);
        OUT(buffer_erreur);
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < (long) total_cells; i++)
        tabcells[i].no = i;

    tablinks = calloc(total_links, sizeof(cell_link));
    if (tablinks == NULL) {
        snprintf(buffer_erreur, sizeof(buffer_erreur) - 1,
                 "Erreur d'allocation des liens "
                 "(%lu liens à allouer).\n", total_links);
        OUT(buffer_erreur);
        free(tabcells);
        exit(EXIT_FAILURE);
    }
    tab_tm =
        calloc(tm_taille_max_x * tm_taille_max_y * tm_taille_max_z,
               sizeof(struct cell_tm));
    if (tab_tm == NULL) {
        snprintf(buffer_erreur, sizeof(buffer_erreur) - 1,
                 "Erreur d'allocation des liens "
                 "(%lu liens à allouer).\n",
                 tm_taille_max_x * tm_taille_max_y * tm_taille_max_z);
        OUT(buffer_erreur);
        free(tabcells);
        free(tablinks);
        exit(EXIT_FAILURE);
    }
    OUT("    Allocations réussies.\n");

    OUT("(3) Lecture du labyrinthe...\n");
    ret =
        laby_lecture(nom_fichier_entree, tabcells, tablinks, buffer_erreur,
                     sizeof(buffer_erreur) - 1);
    if (ret) {
        snprintf(buffer_erreur, sizeof(buffer_erreur) - 1,
                 "Erreur de lecture du "
                 "du fichier labyrinthe :\n%s", buffer_erreur);
        OUT(buffer_erreur);
        free(tab_tm);
        free(tabcells);
        free(tablinks);
        exit(EXIT_FAILURE);
    }
    OUT("    Lecture réussie.\n");

    OUT("(4) Génération du script...\n");

    if (transferer_dans_tableau_tm
        (tabcells, total_cells, largeur, longueur, hauteur, tab_tm,
         tm_taille_max_x, tm_taille_max_y, tm_taille_max_z,
         opt_silent_mode, buffer_erreur, sizeof(buffer_erreur) - 1)) {
        OUT(buffer_erreur);
        free(tablinks);
        free(tabcells);
        free(tab_tm);
        exit(EXIT_FAILURE);
    }


    if (inventaire_de_tous_les_blocs(&nb_objets,
                                     tab_objets,
                                     MAX_OBJETS,
                                     tab_tm,
                                     tm_taille_max_x,
                                     tm_taille_max_y,
                                     tm_taille_max_z,
                                     buffer_erreur,
                                     sizeof(buffer_erreur) - 1)) {
        OUT(buffer_erreur);
        free(tablinks);
        free(tabcells);
        free(tab_tm);
        exit(EXIT_FAILURE);
    }
    snprintf(buffer_tmp, sizeof(buffer_tmp) - 1,
             "    Nombre de blocs différents : %ld\n", nb_objets);
    OUT(buffer_tmp);
    /* inventaire fini */

    /* ecriture du fichier autohotkey */
    if (output_autohotkey(nom_fichier_sortie, largeur, longueur, hauteur,
                          rewrite_fichier_sortie,
                          tab_objets,
                          nb_objets,
                          tab_tm,
                          tm_taille_max_x, tm_taille_max_y,
                          tm_taille_max_z, buffer_erreur,
                          sizeof(buffer_erreur) - 1)) {
        OUT(buffer_erreur);
        free(tablinks);
        free(tabcells);
        free(tab_tm);
        exit(EXIT_FAILURE);
    }

    free(tablinks);
    free(tabcells);
    free(tab_tm);
    sprintf_time_desc(buffer_tmp, sizeof(buffer_tmp) - 1,
                      "-   Fin du programme");
    OUT(buffer_tmp);
    exit(EXIT_SUCCESS);
}
