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

#include "utils.h"
#include "cell.h"

#define PAS_X 1
#define PAS_Y 1
#define MAX_LONGUEUR_ORDRES (unsigned long)60

char ordres[200] = "";

void ecrire_ordres(FILE ** houtput)
{
    if (strlen(ordres)) {
        fprintf(*houtput, "TMExecute( \"%s\" )\n", ordres);
        ordres[0] = 0;
    }
}

void ajouter_ordres(FILE ** houtput, char *nouveaux_ordres,
                    unsigned long max_longueur)
{
    strcat(ordres, nouveaux_ordres);
    if (strlen(ordres) > max_longueur) {
        ecrire_ordres(houtput);
    }
}

int ouvrir_fichier_ecriture(char *nom_fichier, FILE ** houtput,
                            unsigned long longueur, unsigned long largeur,
                            unsigned long hauteur)
{
    *houtput = fopen(nom_fichier, "wb+");
    if (*houtput == NULL) {
        printf("Erreur d'ecriture dans le fichier <%s>.\n", nom_fichier);
        exit(EXIT_FAILURE);
    }

    fprintf(*houtput, ";-------------------------------\n");
    fprintf(*houtput, "; Fichier de génération de labyrinthe\n;\n");
    fprintf(*houtput, "; (c) Olivier Pons\n;\n");
    fprintf_time_desc(*houtput, "; fichier généré ");
    fprintf(*houtput, ";\n");

    fprintf(*houtput, "#Include Tm.ahk\n\n");
    fprintf(*houtput, "TailleX := %lu\n", largeur);
    fprintf(*houtput, "TailleY := %lu\n", longueur);
    fprintf(*houtput, "TailleZ := %lu\n\n", hauteur);
    fprintf(*houtput, ";-----------------------------------\n");
    fprintf(*houtput, "\n");
    fprintf(*houtput, "ActiverFenetre( \"TrackMania\" )\n");
    fprintf(*houtput, "Sleep, 3000\n");
    fprintf(*houtput, "TMDepartSelectionner()\n");
    fprintf(*houtput, "TMDepartPoser()\n");
    fprintf(*houtput, "TMCarresARelierSelectionner()\n");
    fprintf(*houtput, "\n");
    fprintf(*houtput, "TMExecute( \"-\" )\n");
    fprintf(*houtput,
            ";TMCarresARelierPoser( TailleX, TailleY, TailleZ )\n");

    return 0;
}

void selectionne_tourne_et_pose(FILE ** houtput, char *ordre_selection,
                                long dir, unsigned long largeur)
{
    unsigned long nb_rotations, i;
    fprintf(*houtput, ordre_selection);
    fprintf(*houtput, "\n");
    if (dir == -1) {
        /* Est */
        nb_rotations = 3;
    } else if (dir == 1) {
        /* Ouest */
        nb_rotations = 1;
    } else if (dir == (-(long) largeur)) {
        /* Sud */
        nb_rotations = 0;
    } else if (dir == (long) largeur) {
        /* Nord */
        nb_rotations = 2;
    } else {
        /* devrait jamais arriver => forcer erreur script */
        fprintf(*houtput, "??????\n");
        nb_rotations = 0;
    }
    fprintf(*houtput, "TMExecute( \"");
    for (i = 0; i < nb_rotations; i++) {
        fprintf(*houtput, "T");
    }
    fprintf(*houtput, "*");
    while ((i++) % 4) {
        fprintf(*houtput, "T");
    }
    fprintf(*houtput, "\" )\n");
}
void ecrire_cellule(unsigned long no, unsigned long tag,
                    unsigned long nb_paths, unsigned long *paths,
                    unsigned long longueur,
                    unsigned long largeur,
                    unsigned long hauteur,
                    unsigned long max_longueur_ordres, FILE ** houtput)
{
    static int no_static = -1;
    unsigned long i, j, k, l;
    long diff;
    unsigned char tracer_liens = 1;
    char dir1, dir2;

    /* trace qu'on pourra enlever pour embrouiller
       le fichier trackmania autohotkey : * 
       * vider buffer avant de mettre commentaires : * 
       ecrire_ordres(houtput);
       fprintf(*houtput, "; %lu (", no);
       for (i = 0; i < nb_paths; i++) {
       fprintf(*houtput, "%lu", paths[i]);
       if (i < (nb_paths - 1)) {
       fprintf(*houtput, ",");
       }
       }
       fprintf(*houtput, ")\n");
     */

    /* déplacer le curseur au centre de la
       cellule */
    if (no_static == -1) {
        /* premier appel = départ => haut gauche */
        ajouter_ordres(houtput, "HRD", max_longueur_ordres);
    } else {
        /* si on change d'étage */
        if (((no - 1) % (largeur * longueur)) == 0) {
            ajouter_ordres(houtput, "HRD++", max_longueur_ordres);
        } else if (((no - 1) % largeur) == 0) {
            /* retour à la ligne */
            for (i = 0; i < (largeur - 1); i++) {
                ajouter_ordres(houtput, "LL", max_longueur_ordres);
            }
            ajouter_ordres(houtput, "DD", max_longueur_ordres);
        } else {
            /* pas retour à la ligne => décaler à droite */
            ajouter_ordres(houtput, "RR", max_longueur_ordres);
        }
    }
    /* ok on est au centre => poser : */
    i = 0;
    j = 0;
    while (i < nb_paths) {
        j = (no - 1) - ((no - 1) % (largeur * longueur));
        k = paths[i] - 1;
        k = k - (k % (largeur * longueur));
        if (j != k) {
            break;
        }
        i++;
    }
    if (i == nb_paths) {
        /* pas de montée ni de descente trouvée */
        ajouter_ordres(houtput, "*", max_longueur_ordres);
    } else {
        /* montée ou descente => elle n'a qu'un seul path */
        if (nb_paths != 2) {
            /* ajouter un ordre inconnu pour générer erreur */
            ajouter_ordres(houtput, "^", max_longueur_ordres);
            return;
        }
        if (j < k) {
            /* montée */
            ecrire_ordres(houtput);
            /* prendre l'autre path pour déterminer la direction
               lors de la pose du tremplin */
            if (i == 1) {
                l = paths[0];
            } else {
                l = paths[1];
            }
            diff = (long) no - (long) l;
            /* poser le saut */
            selectionne_tourne_et_pose(houtput, "TMSautSelectionner()",
                                       diff, largeur);
            if (diff == -1) {
                dir1 = 'L';
                dir2 = 'R';
            } else if (diff == 1) {
                dir1 = 'R';
                dir2 = 'L';
            } else if (diff == (-(long) largeur)) {
                dir1 = 'U';
                dir2 = 'D';
            } else if (diff == (long) largeur) {
                dir1 = 'D';
                dir2 = 'U';
            } else {
                dir1 = '?';
                dir2 = '?';
            }
            /* rajouter le complément pour qu'il n'y ait pas
               de trou entre le saut et le circuit au dessus  */
            fprintf(*houtput, "TMExecute( \"%c+\")\n", dir1);
            selectionne_tourne_et_pose(houtput,
                                       "TMSautComplementSelectionner()",
                                       diff, largeur);
            fprintf(*houtput, "TMExecute(\"%c-\")\n", dir2);
            fprintf(*houtput, "TMCarresARelierSelectionner()\n");
        } else {
            fprintf(*houtput, "; Descente => on ne fait rien\n");
            tracer_liens = 0;
        }
    }
    /*
       grosse note pour le "tag>20" :
       => exemple de problème :
       le saut va vers le nord => la destination
       du saut ira aussi vers le nord
       => appelons dest la cellule du haut qui
       va vers le nord. la cellule dest est
       reliée au nord avec une autre cellule,
       appelons "dest nord".
       => lorsqu'on arrive sur "dest nord" on voit 
       qu'elle a un lieu au sud et on trace
       => problème : le bloc posé est directement
       "collé" au tremplin => il ne faut
       *pas* poser le bloc au sud
       => Solution : lors de la création du fichier de base,
       je crée dès le début les "forcages" 
       j'ai donc marqué les deux cellules du haut :
       la destination du saut et le seul lien du
       de cette "dest" avec un tag > 20. 
       => si on rencontre un tag > 20 :
       ça signifie que s'il va vers le sud
       ou l'est il ne faut pas le tracer :
       21 = Nord, 22 = Est, 23 = Sud, 24 = Ouest
     */
    if (tracer_liens) {
        for (i = 0; i < nb_paths; i++) {
            /* (!) on n'a besoin que de poser
               l'Est et le Sud
             */
            diff = (long) no - (long) paths[i];
            /* Est */
            if (diff == -1) {
                if (tag != 22) {
                    ajouter_ordres(houtput, "R*L", max_longueur_ordres);
                }
            }
            /* Sud */
            if (diff == (-(long) largeur)) {
                if (tag != 23) {
                    ajouter_ordres(houtput, "D*U", max_longueur_ordres);
                }
            }
        }
    }
    /* se souvenir de la dernière cellule,
       pour le prochain appel */
    no_static = no;
}

int main(int argc, char *argv[])
{
    FILE *hinput;
    FILE *houtput = NULL;

    char s[250];
    char *t;
    size_t len;
    unsigned long i, j, k, ok;
    unsigned long total_cells = 0;
    unsigned long longueur = 0;
    unsigned long largeur = 0;
    unsigned long hauteur = 0;
    unsigned long max_longueur_ordres = MAX_LONGUEUR_ORDRES;

    unsigned long no_courant = 0;
    unsigned long tag_courant = 0;
    unsigned long nb_paths = 0;
    unsigned long paths[30];

    if (argc != 3) {
        printf("%s [nom fichier maze référence] [nom fichier sortie]\n",
               argv[0]);
        exit(EXIT_FAILURE);
    }

    hinput = fopen(argv[1], "rb");
    if (hinput == NULL) {
        printf("Erreur de lecture du fichier <%s>.\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    printf_time_debut_programme();

    printf("    Génération des macros "
           "Autohotkey pour Trackmania...\n");

    while (fgets(s, sizeof(s), hinput) != NULL) {
        len = strlen(s);
        if (len == 0)
            continue;

        /* supprimer dernier char = retour chariot : */
        if (s[len - 1] < 20)
            s[len - 1] = 0;

        /* ignorer les espaces et tabulations du début */
        t = s;
        while (t[0]) {
            if ((t[0] != ' ') && (t[0] != 9))
                break;
            t++;
        }
        if (strlen(t) == 0)
            continue;
        if (t[0] == ';')
            continue;

        /* arrivé ici, chaine valide => analyse */

        /* déclaration de paramètres */

        /* déclaration de la largeur en tant que paramètre */
        ok = sscanf(t, "param:largeur=%lu", &i);
        if (ok == 1) {
            if (largeur) {
                printf("Erreur : deux fois déclaration de la largeur.\n");
                fclose(hinput);
                fclose(houtput);
                exit(EXIT_FAILURE);
            }
            largeur = i;
        }

        /* déclaration de la longueur en tant que paramètre */
        ok = sscanf(t, "param:longueur=%lu", &i);
        if (ok == 1) {
            if (longueur) {
                printf
                    ("Erreur : deux fois déclaration "
                     "de la longueur.\n");
                fclose(hinput);
                fclose(houtput);
                exit(EXIT_FAILURE);
            }
            longueur = i;
        }

        /* déclaration de la hauteur en tant que paramètre */
        ok = sscanf(t, "param:hauteur=%lu", &i);
        if (ok == 1) {
            if (hauteur) {
                printf("Erreur : deux fois déclaration "
                       "de la hauteur.\n");
                fclose(hinput);
                fclose(houtput);
                exit(EXIT_FAILURE);
            }
            hauteur = i;
        }

        /* déclaration du nombre de cellules composant le labyrinthe */
        ok = sscanf(t, "total_cells=%lu", &i);
        if (ok == 1) {
            if (total_cells) {
                printf
                    ("Erreur : deux fois déclaration "
                     "du nombre de cellules.\n");
                fclose(hinput);
                fclose(houtput);
                exit(EXIT_FAILURE);
            }
            total_cells = i;
        }

        /* pour chaque cellule deux possibilités :
           avec ou sans tag */
        /* forcer k=0 car sinon k garde dernière valeur */
        k = 0;
        /* précaution car sscanf prend aussi ":w=" */
        if (strstr(t, ":p=") != NULL) {
            ok = sscanf(t, "%lu:p=%lu/%lu", &i, &j, &k);
            if (ok == 0) {
                ok = sscanf(t, "%lu:p=%lu", &i, &j);
            }
        } else {
            ok = 0;
        }
        if (ok) {
            if ((largeur == 0) ||
                (longueur == 0) || (hauteur == 0) || (total_cells == 0)) {
                printf("Erreur : déclaration d'un lien trop tôt.\n");
                if (largeur == 0) {
                    printf("La largeur n'est pas encore déclarée.\n");
                }
                if (longueur == 0) {
                    printf("La longueur n'est pas encore déclarée.\n");
                }
                if (hauteur == 0) {
                    printf("La hauteur n'est pas encore déclarée.\n");
                }
                if (total_cells == 0) {
                    printf("Le nombre total de cellules "
                           "n'est pas encore déclaré.\n");
                }
                fclose(hinput);
                fclose(houtput);
                exit(EXIT_FAILURE);
            }
            if (i != no_courant) {
                if (no_courant > 0) {
                    /* fin de lecture d'une cellule */
                    if (houtput == NULL) {
                        ouvrir_fichier_ecriture(argv[2], &houtput,
                                                longueur, largeur,
                                                hauteur);
                    }
                    ecrire_cellule(no_courant, tag_courant, nb_paths,
                                   paths, longueur,
                                   largeur, hauteur, max_longueur_ordres,
                                   &houtput);
                    /* => remise à zéro de la cellule en cours */
                    no_courant = tag_courant = nb_paths = 0;
                }
            }
            /* ajout des informations qu'un vient de lire */
            no_courant = i;
            paths[nb_paths++] = j;
            if (k) {
                tag_courant = k;
            }
        }
    }
    if (no_courant > 0) {
        /* fin de lecture d'une cellule */
        ecrire_cellule(no_courant, tag_courant, nb_paths,
                       paths, longueur, largeur, hauteur,
                       max_longueur_ordres, &houtput);
        /* => remise à zéro de la cellule en cours */
        no_courant = tag_courant = nb_paths = 0;
    }
    /* vider buffer ordres */
    ecrire_ordres(&houtput);
    fprintf(houtput,
            "TMFaireMurAutourLaby( TailleX, TailleY, TailleZ )\n");
    fprintf(houtput,
            ";TMPoserCarresSurVide( TailleX, TailleY, TailleZ )\n");
    fprintf(houtput,
            ";TMPoserLumieresSurVide( TailleX, TailleY, TailleZ )\n");
    fprintf(houtput, "TMFinSelectionner()\n");
    fprintf(houtput, "TMFinPoser( TailleX, TailleY, TailleZ )\n");
    fprintf(houtput, "Exit,\n");

    fclose(hinput);
    fclose(houtput);

    printf("    Génération terminée.\n");
    printf_time_fin_programme();
    exit(EXIT_SUCCESS);
}
