/**
© Florian Joncour, 2013-2018 florian@zetta-sys.com

Ce logiciel est un programme informatique faisant interface à la bibliothèque
Perspective3D, un outil de modélisation 3D à partir de vues orthographiques 2D.

Ce logiciel est régi par la licence CeCILL-C soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiq#ues approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL-C, et que vous en avez accepté les
termes.
**/

#include <QApplication>
#include <QFile>
#include <QFile>
#include <QDir>
#include <QThread>
#include <QStringRef>
#include <QStringList>
#include <QLocale>
#include "API/Qt/Utils.h"

#ifdef DEBUG
    #include <QDebug>
    //#define DEBUG_AUTOMATE
#endif // DEBUG

#include "animeQt.h"
#include "automate.h"

#ifdef SUPPORT_DICTION
    #include "qDicte.h"
#endif // SUPPORT_DICTION

static const QLocale Locale(QLocale::C); // QLocale::French

#define PRECISION_DECIMAUX 6
#define STR_SAUT_LIGNE "\r\n"

#define CHAR_ENV ':'
#define STR_ENV ":"

#define CHAR_COMMENTAIRE '#'
#define STR_COMMENTAIRE "#"

const unsigned int TempsSommeilTempo = 15; /* Temporisation du script (plus la valeur est élevée, plus il y aura du temps de délai entre chaque commande. */

namespace LangageAutomate
{
    const int Version = 100;

    const char StrCommentaire[2]   =  { CHAR_COMMENTAIRE, '\0' };
    const char StrEnvironnement[2] =  { CHAR_ENV, '\0' };

    const char *OuvertureBlocInstruction = "{";
    const char *FermetureBlocInstruction = "}";

    const int ProfondeurMaxExecBlocs = 256; /* Taille maximum de la pile d'exécution (en nombre de blocs d'exécution) */

    struct env
    {
            const char *mot;
            int var;
    };

    struct sym
    {
            const char *mot_fr;
            const char *mot_en;
            int cmd;
    };
    typedef struct sym Sym;

    /* Environnement (n'est pas soumis à une langues particulière). */
    enum EnvironnementLangage { ENV_NUL=-1, VAR_ENV_LANGUE_FR=FRANCAIS, VAR_ENV_LANGUE_EN=ANGLAIS, ENV_LANGUE, ENV_VERSION, ENV_ANIMATION, ENV_NAVIGATION, ENV_COMPTEUR_FICHIER, ENV_INCLURE_FICHIER };

    const env Environnement_Str[] = {
        { STR_ENV "lang", ENV_LANGUE },
        { STR_ENV "lang_ver", ENV_VERSION },
        { STR_ENV "anim", ENV_ANIMATION },      /* Active l'animation lors de l'exécution du script. */
        { STR_ENV "nav3d", ENV_NAVIGATION },    /* Autorise ou pas la navigation entre les vues 3D (utile pour ne pas permettre la fermeture des vues 3D existantes lors de l'exécution du script). */
        { STR_ENV "id", ENV_COMPTEUR_FICHIER },
        { STR_ENV "inc", ENV_INCLURE_FICHIER },
        { "fr", VAR_ENV_LANGUE_FR },
        { "en", VAR_ENV_LANGUE_EN }
    };

    /* Commandes */
    enum CommandesLangage { CMD_NUL=0, CMD_DECORATION, CMD_MESSAGE, CMD_DICTE, CMD_NOTE,  CMD_CADRAGE, CMD_CONFIRMATION, CMD_REINIT, CMD_REINIT_VUES, CMD_OUVRIR,
                            CMD_FERMER2D, CMD_ZONE2D, CMD_ONGLET3D, CMD_VUE, CMD_ORIGINE, CMD_GEN3D, CMD_FERMER3D, CMD_DELAI, CMD_CHARGE_CONFIG,
                            CMD_PARAM, CMD_FORCE_CONFIG, CMD_GEN3D_BLOQUANT, CMD_GEN3D_NON_BLOQUANT, CMD_EXPORT3D, CMD_ANIME_3D, CMD_DEF_BLOC, CMD_EXEC_BLOC,
                            CMD_VAR_CALC, CMD_EXEC_CALC, CMD_BOUCLE_CALC, CMD_COND_CALC, CMD_QUITTE, CMD_ARRET, CMD_QUESTION,
                            TRACE_GROUPE, TRACE_COULEUR, TRACE_LIGNE, TRACE_LIGNE_REL, TRACE_CERCLE, TRACE_ELLIPSE, TRACE_COURBE, TRACE_RECT, TRACE_TEXTE, TRACE_POINT,
                            ARG_ABSOLU, ARG_RELATIF, ARG_VUEFACE, ARG_VUECOTE, ARG_VUEHAUT, ARG_VUEEXTRUSION, ARG_VUEREVOLUTION, ARG_VUEMULT,
                            ARG_QUESTION_BOOL, ARG_QUESTION_INT, ARG_QUESTION_FLOAT,
                            PARAM_TOLERANCE, PARAM_ARRONDI, PARAM_DIVIS_COURBES, PARAM_PROJS, PARAM_INFOS,
                            PARAM_TAILLE_TEXTE_3D, PARAM_FILAIRE, PARAM_MULTI_THREADING, PARAM_PARAMS3D,
                            PARAM_TAILLE_MAX_SURFACES, PARAM_NORME_VUES, PARAM_ID_RAL, PARAM_DIAGO_ECHELLE_UNIFORME };

    enum Contextes { CONTEXTE_CMD=0, CONTEXTE_TRACE, CONTEXTE_ARGUMENT, CONTEXTE_PARAM };

    const sym Commandes_Str[] = /* Commandes */
    {
    { StrCommentaire,  StrCommentaire,       CMD_NUL },
    { "arret",         "exit",               CMD_ARRET },               /* Arrêt définitif. Sera compté comme le signalement d'une erreur. */
    { "quitte",        "break",              CMD_QUITTE },              /* Arrêt du bloc d'exécution ou du script suivant le contexte (n'est pas forcément une erreur). */
    { "message",       "print",              CMD_MESSAGE },             /* Commande demande l'affichage d'un message */
    { "dicte",         "say",                CMD_DICTE },               /* Commande demande la diction d'un message audio (Zetta6 doit être compilé avec le support de la diction) */
    { "note",          "note",               CMD_NOTE },                /* Commande demande l'affichage d'une message discret */
    { "cadrage2D",     "fitintview",         CMD_CADRAGE },             /* Commande demande le cadrage de la vue 2D sur la scène */
    { "confirmation",  "confirm",            CMD_CONFIRMATION },        /* Affiche un message (personnalisé) de confirmation à l'utilisateur. Si il accepte, le script se poursuit, sinon il est arrêté. */
    { "reinit",        "reset",              CMD_REINIT },              /* Commande de réinitialisation, supprime tous les modèles 3D, et supprime tous les cadres de la vue 2D ainsi que les origines. */
    { "reinit2D",      "2Dreset",            CMD_REINIT_VUES },         /* Commande de réinitialisation (vues et origines 2D uniquement). */
    { "ouvrir_plan",   "open",               CMD_OUVRIR },              /* Ouvre un plan (relativement au répertoire du script à son ouverture). */
    { "fermer_plan",   "close",              CMD_FERMER2D },            /* Ferme le plan 2D en cours. */
    { "onglet2D",      "2Dtab",              CMD_ZONE2D },              /* Passe sur la vue 2D. */
    { "onglet3D",      "3Dtab",              CMD_ONGLET3D },            /* Passe sur une vue 3D (doit être suivi d'un entier étant le numéro de la vue 3D, la première étant identifiée 1). */
    { "vue",           "frame",              CMD_VUE },                 /* Défini un cadre de vue 2D. */
    { "origine",       "origin",             CMD_ORIGINE },             /* Défini une origine 2D. */
    { "gen3D",         "3Dgen",              CMD_GEN3D },               /* Demande la génération 3D. L'exécution du script sera bloquée le temps de la génération du solide. */
    { "gen3Db",        "3DgenL",             CMD_GEN3D_BLOQUANT },      /* Génère un modèle 3D en bloquant l'exécution du script tant que l'utilisateur ne repasse pas sur la vue 2D. */
    { "gen3Dnb",       "3DgenNL",            CMD_GEN3D_NON_BLOQUANT },  /* Génèration 3D non bloquante. */
    { "export3d",      "export3d",           CMD_EXPORT3D },            /* Génèration 3D non bloquante. */
    { "fermer3D",      "3Dclose",            CMD_FERMER3D },            /* Ferme la dernière vue 3D. */
    { "delai",         "sleep",              CMD_DELAI },               /* Délai avant exécution de la commande suivante (en ms) */
    { "param",         "set",                CMD_PARAM },               /* Changement des paramètres (interne au script) */
    { "charge_config", "load_settings",      CMD_CHARGE_CONFIG },       /* Chargement des paramètres utilisateur (indispensable avant de les modifier) */
    { "force_config",  "overwrite_settings", CMD_FORCE_CONFIG },        /* Force l'écriture des paramètres dans la configuration utilisateur. Sera soumis à l'approbation de l'utilisateur. */
    { "anime3D",       "3Danim",             CMD_ANIME_3D },            /* Lance l'animation de la scène sur une vue 3D actuelle. Ignoré si l'on est sur la vue 2D. */
    { "bloc",          "func",               CMD_DEF_BLOC },            /* Définition d'un bloc de code. */
    { "exec",          "exec",               CMD_EXEC_BLOC },           /* Exécution d'un bloc. */
    { "var",           "var",                CMD_VAR_CALC },            /* Définition d'une variable dans Zcalc. */
    { "calc",          "calc",               CMD_EXEC_CALC },           /* Evaluation dans Zcalc. */
    { "boucle",        "loop",               CMD_BOUCLE_CALC},          /* Opération de répétition dans Zcalc. Prend deux arguments, le premier est destiné à l'évaluation et doit être entre guillements, si la condition est remplie, le second argument doit fournir un nom de bloc qui sera exécuté par répétition tant que la condition est remplie. */
    { "si",            "if",                 CMD_COND_CALC },           /* Opération de comparaison dans Zcalc. Prend deux ou trois arguments, le premier est destiné à l'évaluation et doit être entre guillements, si la condition est remplie, le second argument doit fournir un nom de bloc qui sera exécuté si la condition est remplie. Dans le cas contraire, le troisième argument (optionnel) sera exécuté. */
    { "question",      "question",           CMD_QUESTION }             /* Question à l'utilisateur (commande suivie du type, du nom de la variable, du texte de la question et de la valeur par défaut). */
    };

    const sym CommandesTrace_Str[] = /* Commandes de tracé */
    {
    { ".groupe",    ".group",     TRACE_GROUPE },       /* Créé un nouveau groupe, les entités ajoutées après en feront partie jusqu'à la définition d'un nouveau groupe. */
    { ".couleur",   ".color",     TRACE_COULEUR },      /* Ajoute une couleur, les entités ajoutées après l'utiliseront jusqu'à la définition d'une nouvelle couleur. */
    { ".ligne",     ".line",      TRACE_LIGNE },        /* Ajout d'une ligne, toutes les coordonnées sont absolues. */
    { ".ligne_rel", ".line_rel",  TRACE_LIGNE_REL },    /* Ajout d'une ligne, les coordonnées du premier point sont absolues, les coordonnées du second point sont relatives. */
    { ".cercle",    ".circle",    TRACE_CERCLE },       /* Ajout d'une cercle (ont doit définir les coordonnées du centre, puis un rayon). */
    { ".ellipse",   ".ellipse",   TRACE_ELLIPSE },      /* Ajout d'une ellipse (on, doit définir les coordonées du centre, les deux rayons et un angle de rotation. */
    { ".courbe",    ".curve",     TRACE_COURBE },       /* Ajout d'une courbe générique (on doit définir une origine, les deux rayons, l'angle de départ, l'angle d'arrivé et un angle de rotation général. */
    { ".rect",      ".rect",      TRACE_RECT },         /* Ajout d'un rectangle. (on doit définir une origine et un second point relatif au premier). */
    { ".texte",     ".text",      TRACE_TEXTE },        /* Ajout d'un texte (on doit définir le texte, l'origine, la hauteur de la police et un angle de rotation). */
    { ".point",     ".point",     TRACE_POINT }         /* Ajout d'un point (on doit définir les deux coordonnées). */
}   ;

    const sym Arguments_Str[] = /* Arguments */
    {
    { "abs",   "abs",    ARG_ABSOLU },                  /* Dans le cas où l'on définit un cadre de vue 2D, on demande à ce que le second point soit en coordonnées absolue. */
    { "rel",   "rel",    ARG_RELATIF },                 /* Dans le cas où l'on définit un cadre de vue 2D, on demande à ce que le second point soit en coordonnées relatives. */
    { "face",  "front",  ARG_VUEFACE },                 /* Défini le cadre comme étant la vue de face. */
    { "cote",  "side",   ARG_VUECOTE },                 /* Défini le cadre comme étant la vue de côté. */
    { "haut",  "top",    ARG_VUEHAUT },                 /* Défini le cadre comme étant la vue de dessus. */
    { "extrusion",  "extrusion",   ARG_VUEEXTRUSION },
    { "revolution",  "revolution",   ARG_VUEREVOLUTION },
    { "mult",  "auto",   ARG_VUEMULT },                 /* Défini le cadre comme étant multiple, la séparation automatique des vues sera mise en oeuvre. */
    { "bool", "bool", ARG_QUESTION_BOOL },              /* Défini le type de la valeur (ici bool) posée en question à l'utilisateur. */
    { "int", "int", ARG_QUESTION_INT },                 /* Défini le type de la valeur (ici int) posée en question à l'utilisateur. */
    { "float", "float", ARG_QUESTION_FLOAT },           /* Défini le type de la valeur (ici float) posée en question à l'utilisateur. */
    };

    const sym ParametresP3D_Str[] = /* Arguments (pour les paramètres) */
    {
    { "tolerance",            "allowable_error",  PARAM_TOLERANCE },                  /* Tolérance aux erreurs (doit être suivi nombre décimal en argument). */
    { "echelle_uniforme",     "uniform_scale",    PARAM_DIAGO_ECHELLE_UNIFORME },     /* Valeur de mise à l'echelle uniforme (diagonale) pour le calcul interne (permet d'uniformiser la valeur de tolérance). */
    { "arrondi",              "round",            PARAM_ARRONDI },                    /* Valeur d'arrondi aux nombres décimaux (doit être suivi d'un nombre entier en argument). */
    { "divisions_courbes",    "cuves_divisions",  PARAM_DIVIS_COURBES },              /* Nombre de divisions pour les entitées courbes (doit être suivi d'un nombre entier en argument) */
    { "projections",          "projections",      PARAM_PROJS },                      /* Configuration des projections des vues par un argument entier qui est la combinaison binaire des vues (nul=0, face=2, côté=4, haut=8; Par exemple 2|4|8 = 14, donc 14 pour afficher toutes les projections) */
    { "informations",         "informations",     PARAM_INFOS },                      /* Paramètres d'informations 3D affichées par un argument entier qui est la combinaison binaire des paramètres définis dans l'espace de nom Perspective3D::INFOS3D dans l'API. */
    { "taille_texte3d",       "3Dtext_size",      PARAM_TAILLE_TEXTE_3D },            /* Taille du texte 3D affiché en % de la taille du solide (doit être suivi d'un nombre entier en argument). */
    { "filaire",              "wireframe",        PARAM_FILAIRE },                    /* Impose l'état de génération filaire uniquement (suivi de 1 pour activer ou 0 pour désactiver). */
    { "multithreading",       "multithreading",   PARAM_MULTI_THREADING },            /* Impose l'état du multithreading (suivi de 1 pour activer ou 0 pour désactiver). Note: Ce paramètre n'a d'effet que si le logiciel est activé par une clé de licence. */
    { "params3D",             "3Dsettings",       PARAM_PARAMS3D },                   /* Paramètres de génération 3D. Doit être suivi de la combinaison binaire des paramètres définis dans l'espace de nom Perspective3D::PARAMS_GEN3D de l'API. */
    { "taille_max_surfaces",  "max_faces_size",   PARAM_TAILLE_MAX_SURFACES },        /* Taille maximum des surfaces en nombre de sommets (doit être suivi d'un nombre entier en argument). */
    { "norme_vues",           "views_type",       PARAM_NORME_VUES },                 /* Type d'orientation des vues (suivi de 1 pour la norme ISO, 2 pour la norme Nord-Américaines). */
    { "id_ral",               "ral_id",           PARAM_ID_RAL }                      /* Identifiant du RAL pour la couleur du solide (doit être suivi d'un nombre entier en argument). */
    };

    const unsigned int N_CommandesLangage = sizeof(Commandes_Str) / sizeof(sym);
    const unsigned int N_CommandesTrace = sizeof(CommandesTrace_Str) / sizeof(sym);
    const unsigned int N_ArgumentsLangage = sizeof(Arguments_Str) / sizeof(sym);
    const unsigned int N_ParametresP3D = sizeof(ParametresP3D_Str) / sizeof(sym);
    const unsigned int N_Environnement = sizeof(Environnement_Str) / sizeof(env);

} /* namespace LangageAutomate */

void SupprimeCharsSpeciauxStr(QString &s)
{
    s.remove(QRegularExpression("[" + QRegularExpression::escape("'!*,?|¡¿\n\r") + "]"));

    if (s.contains(QRegularExpression("[" + QRegularExpression::escape("$/:ÀÁÄÙÛÜàáäçèéêëïñóöùûü") + "]")))
    {
        s.replace(QRegularExpression("[" + QRegularExpression::escape(":/\\ ") + "]"), "_");
        s.replace(QRegularExpression("[$]([^0-9])"), "s\\1");

        s.replace(QRegularExpression("[ÁÀ]"),   "A");
        s.replace(QRegularExpression("[Ä]"),    "Ae");
        s.replace(QRegularExpression("[ÜÛÙ]"),  "U");

        s.replace(QRegularExpression("[áà]"),   "a");
        s.replace(QRegularExpression("[ä]"),    "ae");
        s.replace(QRegularExpression("[ç]"),    "c");
        s.replace(QRegularExpression("[ëêéè]"), "e");
        s.replace(QRegularExpression("[ï]"),    "i");
        s.replace(QRegularExpression("[ñ]"),    "n");
        s.replace(QRegularExpression("[óö]"),   "o");
        s.replace(QRegularExpression("[ûù]"),   "u");
        s.replace(QRegularExpression("[ü]"),    "ue");
    }
}

const char *EnvironnementId(int var)
/* Renvoi la chaine de caractère représentant la commande de variable d'environnement (d'après son identifiant). */
{
    for(unsigned int i=0; i<LangageAutomate::N_Environnement; ++i)
    {
        if (var == LangageAutomate::Environnement_Str[i].var)
        {
            return LangageAutomate::Environnement_Str[i].mot;
        }
    }
    return 0;
}

const char *CommandeId(int contexte, int cmd, int langue)
/* Renvoi la chaine de caractère représentant la commande (d'après son identifiant) donnée en argument */
{
    const LangageAutomate::sym *t = ((contexte == LangageAutomate::CONTEXTE_CMD) ? LangageAutomate::Commandes_Str :
                                    ((contexte == LangageAutomate::CONTEXTE_TRACE) ? LangageAutomate::CommandesTrace_Str :
                                    ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::Arguments_Str :
                                        LangageAutomate::ParametresP3D_Str)));

    const unsigned int n_t = ((contexte == LangageAutomate::CONTEXTE_CMD) ? LangageAutomate::N_CommandesLangage :
                             ((contexte == LangageAutomate::CONTEXTE_TRACE) ? LangageAutomate::N_CommandesTrace :
                             ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::N_ArgumentsLangage :
                                                                         LangageAutomate::N_ParametresP3D)));

    for(unsigned int i=0; i<n_t; ++i)
    {
        if (cmd == t[i].cmd)
        {
            if (langue == LangageAutomate::FRANCAIS) { return t[i].mot_fr; }
            else if (LangageAutomate::ANGLAIS) { return t[i].mot_en; }
            else { break; }
        }
    }
    return 0;
}

inline bool cmp_automate(const QString &cmd, const char *s)
{
    return (cmd.compare(s, Qt::CaseInsensitive) == 0);
}

inline bool cmp_automate(const QStringRef cmd, const char *s)
{
    return (cmd.compare(QString(s), Qt::CaseInsensitive) == 0);
}

inline bool cmp_automate(const QString &s1, const QString &s2)
{
    return (s1.compare(s2, Qt::CaseInsensitive) == 0);
}

inline QStringRef extrait_mot(const QString &str)
{
    for(int i=0; i<str.size(); ++i)
    {
        if (str[i] != ' ')
        {
            return str.midRef(i, str.size()-i);
        }
    }
    return str.leftRef(str.size());
}

class BlocCodeAutomate
{
    public:
        inline explicit BlocCodeAutomate(const QString &n) : nom(n), id_ligne_debut(-1), id_ligne_fin(-1) { }
        inline BlocCodeAutomate() : nom(QString()), id_ligne_debut(-1), id_ligne_fin(-1) { }

        inline BlocCodeAutomate(const BlocCodeAutomate &b) : nom(b.nom), id_ligne_debut(b.id_ligne_debut), id_ligne_fin(b.id_ligne_fin) { }

        inline bool ValideNom() const
        {
            return !nom.isEmpty();
        }

        const QString &Nom() const
        {
            return nom;
        }

        inline bool ValideLigneDebut() const
        {
            return id_ligne_debut != -1;
        }

        inline void defLigneDebut(int id)
        {
            id_ligne_debut = id;
        }

        inline int LigneDebut() const
        {
            return id_ligne_debut;
        }

        inline bool ValideLigneFin() const
        {
            return id_ligne_fin != -1;
        }

        inline int LigneFin() const
        {
            return id_ligne_fin;
        }

        inline void defLigneFin(int id)
        {
            id_ligne_fin = id;
        }

        inline int NombreInstructions() const
        {
            return (id_ligne_fin - id_ligne_debut) + 1;
        }

    protected:
        QString nom;
        int id_ligne_debut;
        int id_ligne_fin;
};

class ExecBlocAutomate
{
        /* Paramètres d'exécution d'un bloc d'instructions. */

    public:
        inline ExecBlocAutomate() : pos_boucle(-1), id_bloc(-1), compteur_ligne(0), id_ligne_retour(-1)
        {
        }

        inline ExecBlocAutomate(int id_bloc_exec, int id_ligne_appel) :
            pos_boucle(-1), id_bloc(id_bloc_exec), compteur_ligne(0), id_ligne_retour(id_ligne_appel+1)
        {
//            id_ligne_retour = id_ligne_appel+1;
        }

        inline void IncCompteurInstruction()
        /* Incrémente le compteur d'instuctions. */
        {
            ++compteur_ligne;
        }

        inline void DecCompteurInstruction()
        /* Décrémente le compteur d'instuctions. */
        {
            --compteur_ligne;
        }

        inline int CompteurInstructions() const
        {
            return compteur_ligne;
        }

        inline int LigneRetour() const
        /* Renvoi la ligne à exécuter après la fin de l'exécution */
        {
            return id_ligne_retour;
        }

        inline int PositionRetourBoucle() const
        /* Position de retour de boucle (pour re-contrôler l'instruction). */
        {
            return pos_boucle;
        }

        inline bool Boucle() const
        {
            return (pos_boucle != -1);
        }

        inline void defPositionBoucle(int pos)
        {
            pos_boucle = pos;
        }

        inline void SupprimeBoucle()
        {
            pos_boucle = -1;
        }

        inline bool ReinitRetourBoucle(const BlocCodeAutomate &b)
        /* Assigne la valeur de retour de boucle dans le compteur d'instruction en fonction du bloc. */
        {
            if (Boucle())
            {
//                compteur_ligne = (pos_boucle - (b.LigneDebut() + 1));
                compteur_ligne = (pos_boucle - b.LigneDebut());
#ifdef DEBUG
                qDebug() << "ReinitRetourBoucle() :: " << compteur_ligne << " (" << pos_boucle << "-" << b.LigneDebut() << ")";
#endif
                return true;
            }
            return false;
        }

        inline void ReinitRetourBoucle()
        {
            pos_boucle = -1;
        }

        inline bool ValideExecution(const BlocCodeAutomate &b) const
        /* Renvoi true si l'exécution du bloc de code doit se poursuivre, sinon false. */
        {
            return (compteur_ligne < b.NombreInstructions());
        }

        inline int IdBloc() const
        /* Renvoi l'id du bloc d'instruction attaché à l'instance courante. */
        {
            return id_bloc;
        }

    protected:
        int pos_boucle;
        int id_bloc;
        int compteur_ligne;
        int id_ligne_retour;
};

class MoteurAutomate
        /* Moteur générique d'un automate à pile. */
{
    public:
        inline explicit MoteurAutomate(const QString &code) : Ligne_nul(""), LignesCode(), Position_Boucle_Main(-1), Compteur_Erreurs(0), Compteur_Lignes(0)
        {
            InsertCode(0, code);

#ifdef DEBUG_AUTOMATE
            Compteur_Affichage = 0;
#endif // DEBUG_AUTOMATE
        }

#ifdef DEBUG_AUTOMATE
#define INDENTATION_AFFICHAGE "    "
        inline void AffichePile(const char *entete)
        {
            ++Compteur_Affichage;
            if (entete)
            {
                qDebug() << "\nPile Automate :" << entete << " (" << Compteur_Affichage << ")\n{";
            }
            else
            {
                qDebug() << "\nPile Automate (" << Compteur_Affichage << ")\n{";
            }

            qDebug() << INDENTATION_AFFICHAGE "Main { Compteur Lignes :" << Compteur_Lignes+1 << "/" << LignesCode.size() << ", Position Boucle :" << (Position_Boucle_Main==-1 ? (-1) : (Position_Boucle_Main+1)) << ", Compteur Erreurs :" << Compteur_Erreurs << "}";

            qDebug() << INDENTATION_AFFICHAGE "Pile [" << pile_exec_blocs.size() << "]:\n" INDENTATION_AFFICHAGE "{";

//            for (int i=pile_exec_blocs.size()-1; i>=0; --i)
            for (int i=0; i<pile_exec_blocs.size(); ++i)
            {
                const ExecBlocAutomate &e = pile_exec_blocs[i];
                const BlocCodeAutomate &b = blocs_code[e.IdBloc()];

                const int id_ligne_courante = e.CompteurInstructions()+b.LigneDebut();
                qDebug() << INDENTATION_AFFICHAGE INDENTATION_AFFICHAGE "[" << i << "] { id_bloc [" << e.IdBloc() << ":" << b.Nom() << "], exec :" << id_ligne_courante+1 << "(" << e.CompteurInstructions()+1 << "/" << b.NombreInstructions() << "), retour =" << e.LigneRetour()+1 << ", pos_boucle =" << (e.PositionRetourBoucle()==-1 ? -1 : e.PositionRetourBoucle()+1) << "::" << LignesCode[id_ligne_courante] << "}";
            }
            qDebug() << INDENTATION_AFFICHAGE "}\n}";
        }
#undef INDENTATION_AFFICHAGE
#endif // DEBUG_AUTOMATE

        inline void Reinit(const QString &code, bool mode_interpreteur=false)
        /* Réinitialise l'automate avec un nouveau code. */
        {
            if (mode_interpreteur) /* Réinitiation (mode interpréteur uniquement) */
            {
                Compteur_Erreurs = 0;
                if (NombreLignesCode())
                {
//                    qDebug() << "Insertion position " << NombreLignesCode();
                    InsertCode(NombreLignesCode(), code);
                }
                else
                {
//                    qDebug() << "Insertion position initiale (0)";
                    InsertCode(0, code);
                }
            }
            else
            {
                LignesCode.clear();
                Compteur_Erreurs = 0;
                Compteur_Lignes = 0;
                IdsLignesCode.clear();
                Position_Boucle_Main = -1;
                pile_exec_blocs.clear();
                blocs_code.clear();
#ifdef DEBUG_AUTOMATE
                Compteur_Affichage = 0;
#endif // DEBUG_AUTOMATE
                InsertCode(0, code);
            }
        }

        inline bool InsertCode(int pos, const QString &code)
        /* Insertion d'un bloc de code dans le code existant */
        {
            if ((pos >= 0 && pos <= LignesCode.size()) || (pos == 0 && LignesCode.size() == 0))
            {
//                QStringList liste_code = code.split(QRegExp("[\r\n]"), QString::KeepEmptyParts);

                const bool insertion_fin = (pos == LignesCode.size() && (LignesCode.size()>0));

                const QStringList liste_code = code.split(QRegExp("[\r\n]"), QString::KeepEmptyParts); /* Coupe les lignes aux caractères '\r' ou '\n' */

                const int n = liste_code.size();
                if (n)
                {
//                    LignesCode.insert(pos, liste_code[i]);
//                    qDebug() << "Insertion : " << l[i];

                    for(int i=n-1; i>=0; --i)
                    {
                        /* Coupe les lignes aux caractères ';' sauf si la chaine est entre guillemets. */
                        const QStringList s_tmp = liste_code[i].split(QRegExp(";(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"), QString::KeepEmptyParts);
                        for (int i2=s_tmp.size()-1; i2>=0; --i2)
                        {
                            if (insertion_fin)
                            {
                                LignesCode.push_back(s_tmp[i2]);
                                IdsLignesCode.push_back(i);
                            }
                            else
                            {
                                LignesCode.insert(pos, s_tmp[i2]);
                                IdsLignesCode.insert(pos, i);
                            }
                        }
                    }

#ifdef DEBUG_AUTOMATE
                    qDebug() << "InsertCode :: Après insertion (" << LignesCode.size() << "): " << LignesCode;
#endif // DEBUG_AUTOMATE

                    Compteur_Lignes = pos;
                }
                return true;
            }
#ifdef DEBUG_AUTOMATE
            qDebug() << "InsertCode :: Position d'insertion invalide (" << pos << "/" << LignesCode.size() << "): " << LignesCode;
#endif // DEBUG_AUTOMATE
            return false;
        }

        inline bool QuitteBloc()
        /* Quitte le bloc d'exécution en cours. */
        {
            if (pile_exec_blocs.size()) /* Si un bloc est en cours d'exécution, on l'arrête */
            {
//                pile_exec_blocs.clear();
                pile_exec_blocs.pop_back();
                return true;
            }
            return false;
        }

        inline int TaillePile() const
        /* Renvo la taille de la pile d'exécution. */
        {
            return pile_exec_blocs.size();
        }

        inline void decLigneCourante()
        /* Reviens à la ligne précédente. */
        {
            if (pile_exec_blocs.size())
            {
                if (pile_exec_blocs.back().CompteurInstructions())
                {
                    pile_exec_blocs.back().DecCompteurInstruction();
                }
            }
            else
            {
                if (Compteur_Lignes)
                {
                    --Compteur_Lignes;
                }
            }
        }

        inline int LigneCourante() const
        /* Renvoi la ligne de code courante. */
        {
            if (pile_exec_blocs.size())
            {
                return blocs_code[pile_exec_blocs.back().IdBloc()].LigneDebut() + pile_exec_blocs.back().CompteurInstructions();
            }

            return Compteur_Lignes;
        }

        inline int LigneReeleFichier(int id_ligne) const
        /* Renvoi la ligne de code rééle dans le fichier. */
        {
            if ((id_ligne < 0) || (id_ligne >= IdsLignesCode.size()))
            {
                return -1;
            }
            return IdsLignesCode[id_ligne];
        }

        inline void AjoutBloc(const BlocCodeAutomate &b)
        /* Ajoute un bloc de code. */
        {
            blocs_code.push_back(b);
//            qDebug() << "Ajout bloc : " << blocs_code.back().Nom();
            Compteur_Lignes = b.LigneFin()+1; /* Place le compteur d'instruction après la définition de bloc. */
        }

        inline const QVector<BlocCodeAutomate> &BlocsCodes() const
        {
            return blocs_code;
        }

        inline void IncCompteurErreurs()
        {
            ++Compteur_Erreurs;
        }

        inline int CompteurErreurs() const
        {
            return Compteur_Erreurs;
        }

        inline bool ValideLigne(int id_ligne) const
        {
            return ((Compteur_Erreurs == 0) && (id_ligne > -1) && (id_ligne < LignesCode.size()));
        }

        inline bool ValideExec() const
        /* Renvoi true si l'exécution doit se poursuivre, sinon false. */
        {
            return ValideLigne(LigneCourante());
        }

        inline bool ExecBloc(const QString &nom_bloc, bool boucle=false)
        /* Demande l'exécution d'un bloc de code. Renvoi true si le bloc en question a été trouvé, sinon false. */
        {
            int id_bloc = -1;

//            for(int i=0; i<blocs_code.size(); ++i)
            for(int i=blocs_code.size()-1; i>=0; --i) /* Parcours depuis la fin... */
            {
                if (cmp_automate(blocs_code[i].Nom(), nom_bloc))
                {
                    id_bloc = i;
                    break;
                }
            }

            if (id_bloc != -1)
            {
                ExecBlocAutomate exe(id_bloc, LigneCourante());

                if (pile_exec_blocs.size())
                {
                    if (boucle) // Le bloc à exécuté l'est à l'intérieur d'une boucle...
                    {
                        if (pile_exec_blocs.back().PositionRetourBoucle() != (LigneCourante())) // Il s'agit de la première itération...
                        {
#ifdef DEBUG_AUTOMATE
                            qDebug() << "   Assignation d'un retour de boucle : " << LigneCourante() << " : " << LigneCourante()-1;
#endif // DEBUG_AUTOMATE
                            pile_exec_blocs.back().defPositionBoucle(LigneCourante()-1); /* Sauvegarde de la position de la boucle (uniquement à la première itération en principe). */
                        }
                    }
                    else
                    {
                    }
                }
                else
                {
                    if (boucle)
                    {
                        Position_Boucle_Main = LigneCourante()-1;
                    }
                    else
                    {
                    }
                }

#ifdef DEBUG_AUTOMATE
                qDebug() << "Exécution du bloc : " << nom_bloc << ", ligne de retour après l'exécution du bloc : " << exe.LigneRetour()+1;
#endif // DEBUG_AUTOMATE

                pile_exec_blocs.push_back(exe); /* A la prochaine itération de la boucle, l'exécution sera redirigée sur le bloc */

#ifdef DEBUG_AUTOMATE
                AffichePile("ExecBloc");
#endif // DEBUG_AUTOMATE

                return true;
            }

            ++Compteur_Erreurs;
            return false;
        }

        inline void FinBoucle()
        /* On demande l'arrêt de la dernière boucle. */
        {
            if (pile_exec_blocs.size())
            {
                pile_exec_blocs.back().SupprimeBoucle();
                pile_exec_blocs.back().IncCompteurInstruction();
            }
            else
            {
                Position_Boucle_Main = -1;
            }
        }

        inline int Exec()
        /* Renvoi la ligne de code à exécuter (gère le contexte). */
        {
            if (pile_exec_blocs.size()) /* Si l'on a des blocs d'instruction empilés... */
            {
#ifdef DEBUG_AUTOMATE
                AffichePile("Cycle (exécution de bloc)");
#endif // DEBUG_AUTOMATE

                while (true) /* Contrôle si l'on doit quitter les blocs d'instructions. */
                {
                    if (pile_exec_blocs.size())
                    {
                        ExecBlocAutomate &exec = pile_exec_blocs.back();
                        const BlocCodeAutomate &bloc = blocs_code[exec.IdBloc()];

                        if (!exec.ValideExecution(bloc) && !exec.Boucle())
                        {
//                            qDebug() << "Retrait du bloc d'instruction" << blocs_code[pile_exec_blocs.back().IdBloc()].Nom() << "de la pile.";
                            pile_exec_blocs.pop_back();
                            continue;
                        }
                        break;
                    }
                    break;
                }
            }

            if (pile_exec_blocs.size()) /* Si il reste des blocs d'instruction empilés... */
            {
                ExecBlocAutomate &exec = pile_exec_blocs.back();
                const BlocCodeAutomate &bloc = blocs_code[exec.IdBloc()];

                if (exec.Boucle())
                {
#ifdef DEBUG_AUTOMATE
                    AffichePile("Cycle (retour de boucle !!!)");
#endif // DEBUG_AUTOMATE
                    exec.ReinitRetourBoucle(bloc);

                    if (exec.LigneRetour() < 0)
                    {
#ifdef DEBUG_AUTOMATE
                        qDebug() << "Erreur automate : Ligne à exécuter invalide (" << exec.LigneRetour() << ")";
#endif // DEBUG_AUTOMATE
                        IncCompteurErreurs();
                        return -2;
                    }

//                        exec.DecCompteurInstruction();
                    return exec.PositionRetourBoucle();
                }
                else /* Pas de boucle dans le bloc courant. */
                {
                    if (!exec.ValideExecution(bloc)) /* Fin d'exécution du bloc. */
                    {
#ifdef DEBUG_AUTOMATE
                        AffichePile("Cycle (fin de bloc)");
                        qDebug() << "  Fin d'exécution du bloc " << bloc.Nom() << " : " << bloc.LigneDebut() + exec.CompteurInstructions() + 1 << " :: Ligne retour=" << exec.LigneRetour()+1 << " :: boucle=" << exec.PositionRetourBoucle()+1 << ", CompteurLigne=" << Compteur_Lignes;
#endif // DEBUG_AUTOMATE
//                        AffichePile();

                        pile_exec_blocs.pop_back();
//                        if (pile_exec_blocs.size())
//                        {
//                            pile_exec_blocs.back().IncCompteurInstruction();
//                        }
                    }
                }

                const int id_ligne = LigneCourante(); // bloc.LigneDebut() + exec.CompteurInstructions();
                if (pile_exec_blocs.size())
                {
                    pile_exec_blocs.back().IncCompteurInstruction();
                }
                else
                {
                    ++Compteur_Lignes;
                }
                return id_ligne;
            }

            if (!ValideExec())
            {
                return -1; /* Ligne nulle car l'exécution ne doit pas se poursuivre. */
            }

            if (Position_Boucle_Main != -1)
            {
                Compteur_Lignes = Position_Boucle_Main+1;
//                qDebug() << "Nouvelle itération de boucle sur le fil principal !!!" << Compteur_Lignes;
                return Compteur_Lignes-1;
            }

            /* Exécution sur la file d'exécution principale: */
//            const QString &s = LignesCode[Compteur_Lignes];
            ++Compteur_Lignes;
            return Compteur_Lignes-1;
        }

        inline const QString &CodeExec()
        {
            return LigneCode(Exec());
        }

        inline const QString &LigneCode(int id) const
        /* Renvoi une ligne de code d'après sont id. */
        {
            if (id >= 0 && id <LignesCode.size())
            {
                return LignesCode[id];
            }
            return Ligne_nul;
        }

        inline int NombreLignesCode() const
        {
            return LignesCode.size();
        }

        inline bool DesactiveLigneCode(int id)
        {
            if (id >= 0 && id < LignesCode.size())
            {
#ifdef DEBUG_AUTOMATE
                LignesCode[id] = QString(LangageAutomate::StrCommentaire) + " " + LignesCode[id];
#else
                LignesCode[id].clear();
#endif // DEBUG_AUTOMATE
                return true;
            }
            return false;
        }

        inline const QStringList &ListeLignesCode() const
        {
            return LignesCode;
        }

    protected:
        QString Ligne_nul;
        QStringList LignesCode;
        QVector<int> IdsLignesCode;
        QVector<BlocCodeAutomate> blocs_code;
        QVector<ExecBlocAutomate> pile_exec_blocs;
        int Position_Boucle_Main;
        int Compteur_Erreurs;
        int Compteur_Lignes;
#ifdef DEBUG_AUTOMATE
        int Compteur_Affichage;
#endif // DEBUG_AUTOMATE
};

Automate::Automate(const QString &chemin_zetta6) : CheminZetta6(chemin_zetta6), Moteur(0), accepte_export3d(false), Calc(this), couleur_trace(0xFFFFFFFF), parametres_zetta6(0)
{
    execution_verbeuse = false;
    en_cours = false;
    animation_script = false;
    execMultithread = false;

    connect(&Tempo, SIGNAL(timeout()), this, SLOT(execTempo()));
    connect(&Calc, SIGNAL(Message_sig(const QString &)), this, SLOT(Message_calc(const QString &)));
}

Automate::~Automate()
{
    if (Moteur)
    {
        delete Moteur;
    }
}

void Automate::defConfig(Parametres *config)
{
    parametres_zetta6 = config;
    if (parametres_zetta6)
    {
        accepte_export3d = parametres_zetta6->R_AutoriseExport3DAutomate();
    }
}

bool Automate::Stop(bool err)
{
    force_arret = true;

    if (Tempo.isActive())
    {
        if (err)
        {
#ifdef DEBUG_AUTOMATE
            qDebug() << "Erreur automate : Appel de la fonction Stop()";
#endif // DEBUG_AUTOMATE
            Moteur->IncCompteurErreurs();
        }
        pause = false;
        Tempo.stop();

        while (ExecEnCours())
        {
            QThread::msleep(10);
        }

        return true;
    }
    en_cours = false;
    return false;
}

bool Automate::ExecEnCours() const
{
    return en_cours;
}

bool Automate::Pause()
{
    if (Tempo.isActive())
    {
        pause = true;
        return true;
    }
    return false;
}

bool Automate::Continue()
{
    if (Tempo.isActive())
    {
        pause = false;
        return true;
    }
    return false;
}

bool Automate::ArretUtilisateur()
{
#ifdef DEBUG_AUTOMATE
    qDebug() << "Automate interrompu...";
#endif // DEBUG_AUTOMATE
    force_arret = true;
    return Stop();
}

bool Automate::AccepteNavigation3D() const
{
    return accepte_nav3d;
}

bool Automate::AccepteExport3D() const
{
    return accepte_export3d;
}

const QString& Automate::DerniereErreur() const
/* Renvoi le message de la dernière erreure générée par l'automate. */
{
    return mesg_derniere_erreur;
}

void Automate::MessageFmtErreur(const QString &mesg, int ligne)
{
    ++ligne; /* Itération automatique de l'id de la ligne pour commencer à partir de 1, vaudra 0 si on veut pas afficher le numéro de ligne. */

    if (ligne)
    {
#ifdef DEBUG_AUTOMATE
        qDebug() << "Automate Erreur ligne" << ligne << ":" << mesg;
#endif // DEBUG_AUTOMATE
        mesg_derniere_erreur = tr("Erreur ligne") + " " + Locale.toString(Moteur->LigneReeleFichier(ligne)) + " : " + mesg;
        emit Message(tr("(Automate)") + " " + mesg_derniere_erreur, true);
    }
    else
    {
#ifdef DEBUG_AUTOMATE
        qDebug() << "(Automate) :" << mesg;
#endif // DEBUG_AUTOMATE
        mesg_derniere_erreur = tr("Erreur ") + " : " + mesg;
        emit Message(tr("(Automate)") + " " + mesg_derniere_erreur + " : " + mesg, true);
    }
}

void Automate::MessageFmt(const QString &mesg, int ligne, bool mesg_utilisateur) const
{
    ++ligne; /* Itération automatique de l'id de la ligne pour commencer à partir de 1, vaudra 0 si on veut pas afficher le numéro de ligne. */

    if (ligne)
    {
#ifdef DEBUG_AUTOMATE
        qDebug() << "Automate ligne" << ligne << ":" << mesg;
#endif // DEBUG_AUTOMATE
        emit Message(tr("(Automate)") + " " + tr("Information ligne") + " " + Locale.toString(Moteur->LigneReeleFichier(ligne)) + " : " + mesg, mesg_utilisateur);
    }
    else
    {
#ifdef DEBUG_AUTOMATE
        qDebug() << "(Automate) :" << mesg;
#endif // DEBUG_AUTOMATE
        emit Message(tr("(Automate)") + " " + tr("Message") + " : " + mesg, mesg_utilisateur);
    }
}

void Automate::AfficheVariable(const QString &var, const QString &val) const
/* Affichage de la valeur d'une variable (typiquement de la part de l'interpréteur. */
{
    MessageFmt((var + " : " + val), -1, false);
}

void Automate::AjoutJournal(const QString &commande, const QStringList &arguments, bool exec_pas_a_pas)
/* Ajout d'une commande en cours dans le journal. */
{
    QString mesg(commande);
    if (arguments.size())
    {
        mesg += " [ ";
        for(int i=0; i<arguments.size(); ++i)
        {
            mesg += arguments[i] + " ";
        }
        mesg += "]";
    }
    emit Message(mesg, exec_pas_a_pas);
}

QString &Automate::FormateMessageUtilisateur(QString &s) const
{
    return s.replace("\\n", "\n");
}

QString Automate::FormateArgumentsMessage(const QStringList &arguments, bool diction) const
{
//    qDebug() << "FormateArgumentsMessage :: " << arguments;

    const int n_args = arguments.size();

//    if (n_args == 1)
//    {
//        QString copie = arguments[0];
//        MessageFmt(FormateMessageUtilisateur(copie), -1, true);
//    }
//    else if (n_args > 1)
    if (n_args > 0)
    {
        QString mesg;
        for(int i=0; i<n_args; ++i)
        {
            if (i)
            {
                mesg += " ";
            }

            const QString &var = arguments[i];

//            QString var = arguments[i];
            /* Recole les éventuels indices de tableaux séparés par des espaces. */
//            if ((i<(n_args-2)) && arguments[i].endsWith('['))
//            {
//                bool valide_recolage = false;

//                for(int i2=i+1; i2<n_args; ++i2)
//                {
//                    var += arguments[i2];

//                    if (arguments[i2].endsWith(']'))
//                    {
//                        i = i2;
//                        valide_recolage = true;
//                        break;
//                    }
//                }

//                if (!valide_recolage)
//                {
//                    var = arguments[i];
//                }
//            }

            QString v = Calc.EncodeValeur(var);

            if (!v.isEmpty())
            {
                if (diction) /* Formate les valeurs flottantes pour la diction */
                {
                    const int pos = v.indexOf('.');
                    if (pos != -1)
                    {
                        v = v.left(pos + 4);
                        if (Langue != LangageAutomate::ANGLAIS)
                        {
                            v.replace(QString("."), QString(","));
                        }
                    }
                }
                mesg += v;
            }
            else
            {
                mesg += arguments[i];
            }
        }
        return mesg;
    }
    return QString();
}

int Automate::LangueDiction() const
{
#ifdef SUPPORT_DICTION
    if (Langue == LangageAutomate::FRANCAIS)
    {
        return qDicte::LANG_FR;
    }
    if (Langue == LangageAutomate::ANGLAIS)
    {
        return qDicte::LANG_EN_GB;
    }
#endif // SUPPORT_DICTION
    return 999;
}

bool Automate::AssigneBool(bool &b, const QString &s) const
{
    if (cmp_automate(s, "1") || ((Langue == LangageAutomate::ANGLAIS) && cmp_automate(s, "true")) || ((Langue == LangageAutomate::FRANCAIS) && cmp_automate(s, "oui")))
    {
        b = true;
        return true;
    }
    else if (cmp_automate(s, "0") || ((Langue == LangageAutomate::ANGLAIS) && cmp_automate(s, "false")) || ((Langue == LangageAutomate::FRANCAIS) && cmp_automate(s, "non")))
    {
        b = false;
        return true;
    }
    else
    {
         if (Calc.ValeurBool(s, b))
         {
             return true;
         }
    }
    return false;
}

bool Automate::AssigneUInt(unsigned int &i, const QString &s) const
{
    bool valide_conv = true;
    int r = Locale.toUInt(s, &valide_conv);
    if (valide_conv)
    {
        i = r;
        return true;
    }
    else
    {
        int d;
        if (Calc.ValeurInt(s, d))
        {
            i = static_cast<unsigned int>(d);
            return true;
        }
    }
    return false;
}

bool Automate::AssigneInt(int &i, const QString &s) const
{
    bool valide_conv = true;
    int r = Locale.toInt(s, &valide_conv);
    if (valide_conv)
    {
        i = r;
        return true;
    }
    else
    {
        if (Calc.ValeurInt(s, i))
        {
            return true;
        }
    }
    return false;
}


bool Automate::AssigneUshort(unsigned short &i, const QString &s) const
{
    bool valide_conv = true;
    int r = Locale.toUShort(s, &valide_conv);
    if (valide_conv)
    {
        i = r;
        return true;
    }
    else
    {
        short d;
        if (Calc.ValeurShort(s, d))
        {
            i = static_cast<unsigned short>(d);
            return true;
        }
    }
    return false;
}

bool Automate::AssigneShort(short &i, const QString &s) const
{
    bool valide_conv = true;
    int r = Locale.toShort(s, &valide_conv);
    if (valide_conv)
    {
        i = r;
        return true;
    }
    else
    {
        if (Calc.ValeurShort(s, i))
        {
            return true;
        }
    }
    return false;
}

bool Automate::AssigneFloat(float &f, const QString &s) const
{
    bool valide_conv = true;
    float r = Locale.toFloat(s, &valide_conv);
    if (valide_conv)
    {
        f = r;
        return true;
    }
    else
    {
        double d=0;
        if (Calc.ValeurFloat(s, d))
        {
            f = static_cast<float>(d);
            return true;
        }
    }
    return false;
}

bool Automate::AssigneDouble(double &d, const QString &s) const
{
    bool valide_conv = true;
    double r = Locale.toDouble(s, &valide_conv);
    if (valide_conv)
    {
        d = r;
        return true;
    }
    else
    {
        if (Calc.ValeurFloat(s, d))
        {
            return true;
        }
    }
    return false;
}

bool Automate::AssignePfloat(pfloat &d, const QString &s) const
{
    bool valide_conv = true;
    pfloat r = Locale.toDouble(s, &valide_conv);
    if (valide_conv)
    {
        d = r;
        return true;
    }
    else
    {
        double dx=0;
        if (Calc.ValeurFloat(s, dx))
        {
            d = static_cast<float>(dx);
            return true;
        }
    }
    return false;
}

bool Automate::AssigneCoords(const QStringList &arguments, float *coords, unsigned int decalage, unsigned int nb_coords) const
/* Assigne les coordonnées d'après les arguments. */
{
    if (arguments.size() < (int) (decalage+nb_coords))
    {
        return false;
    }
    for(unsigned int ix=0; ix<nb_coords; ++ix)
    {
        if (!AssigneFloat(coords[ix], arguments[ix+decalage]))
        {
            return false;
        }
    }
    return true;
}

bool Automate::InclureFichier(int id_ligne_courante, const QString &argument)
{
    if (!Moteur)
        return false;

    const QString s0("");
    const QString s1 = CheminReference + QDir::separator() + argument;
    const QString s2 = CheminZetta6 + QDir::separator() + argument;
    const QString &chemin = QFile::exists(s1) ? (s1) : (QFile::exists(s2) ? s2 : s0);

    if (chemin.isEmpty())
    {
        return false;
    }

    QFile f(chemin);
    if (f.open(QFile::ReadOnly | QFile::Text))
    {
        Moteur->DesactiveLigneCode(id_ligne_courante); /* Avant l'insertion ! */
//        qDebug() << "Insertion position :: " << id_ligne_courante;

        if (Moteur->InsertCode(id_ligne_courante, QString::fromUtf8(f.readAll())))
        {
            f.close();
            return true;
        }
        f.close();
#ifdef DEBUG_AUTOMATE
        qDebug() << "Erreur lors de l'insertion du fichier " << argument << " à la position " << id_ligne_courante << " ?!";
#endif // DEBUG_AUTOMATE
        return false;
    }
    else
    {
#ifdef DEBUG_AUTOMATE
        qDebug() << "Impossible d'ouvrir le fichier " << argument << " ?!";
#endif // DEBUG_AUTOMATE
    }
    return false;
}

bool Automate::ChargeConfig()
/* Recharge les paramètres dans la configuration.
    Si le script défini de nouveaux paramètres, ils seront surchargés sans toucher à la configuration principale */
{
//    qDebug() << "ChargeConfig :: Paramètres Zetta6 : " << (void*) parametres_zetta6;
    if (parametres_zetta6)
    {
        parametres_zetta6->ParamsPerspective(parametres_p3d);
        config_chargee = true;
        return true;
    }
    return false;
}

int Automate::execFichier(const QString &nfichier, bool desactive_temporisation)
/* Ouvre et exécute un fichier */
{
    QFile f_scr(nfichier);
    if (f_scr.open(QFile::ReadOnly | QFile::Text))
    {
        QString script = QString::fromUtf8(f_scr.readAll());
        f_scr.close();

        QDir chemin(nfichier);
        chemin.cdUp();
        return exec(script, chemin.canonicalPath(), desactive_temporisation);

    }
    MessageFmtErreur(tr("Impossible d'ouvrir le fichier") + " " + nfichier);
    return AUTOMATE_ERR;
}

int Automate::EnvironnementAutomate(const QString &mot) const
{
    for(unsigned int i=0; i<LangageAutomate::N_Environnement; ++i)
    {
        if (cmp_automate(mot, LangageAutomate::Environnement_Str[i].mot))
        {
            return LangageAutomate::Environnement_Str[i].var;
        }

    }
    return LangageAutomate::ENV_NUL;
}

int Automate::CommandeAutomate(const QString &mot, int contexte) const
/* Renvoi la commande de l'automate correspondant au mot donné en argument. */
{
    QStringRef mot_r = extrait_mot(mot);

    if (cmp_automate(mot, LangageAutomate::OuvertureBlocInstruction) || cmp_automate(mot, LangageAutomate::FermetureBlocInstruction))
    {
        return LangageAutomate::CMD_DECORATION;
    }

    const bool trace = mot[0] == '.';

    const LangageAutomate::sym *t = ((contexte == LangageAutomate::CONTEXTE_CMD || contexte == LangageAutomate::CONTEXTE_TRACE) ? (trace ? LangageAutomate::CommandesTrace_Str : LangageAutomate::Commandes_Str) :
                                    ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::Arguments_Str :
                                                                                        LangageAutomate::ParametresP3D_Str));

    const unsigned int n_t = ((contexte == LangageAutomate::CONTEXTE_CMD || contexte == LangageAutomate::CONTEXTE_TRACE) ? (trace ? LangageAutomate::N_CommandesTrace : LangageAutomate::N_CommandesLangage) :
                             ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::N_ArgumentsLangage :
                                                                         LangageAutomate::N_ParametresP3D));

    if (Langue == LangageAutomate::FRANCAIS)
    {
        for(unsigned int i=0; i<n_t; ++i)
        {
            if (cmp_automate(mot_r, t[i].mot_fr))
            {
                return t[i].cmd;
            }

        }
    }
    else if (Langue == LangageAutomate::ANGLAIS)
    {
        for(unsigned int i=0; i<n_t; ++i)
        {
            if (cmp_automate(mot_r, t[i].mot_en))
            {
                return t[i].cmd;
            }
        }
    }
    return LangageAutomate::CMD_NUL;
}

int Automate::exec(const QString &code, const QString &chemin_reference, bool desactive_temporisation)
/* Exécute un script. Attention, si chemin_reference est vide, le répertoire courant (d'après l'environnement) sera utilisé pour trouver les fichier avec la commande 'ouvrir_plan' */
{
    if (Tempo.isActive()) // Un script est déjà en cours d'exécution.
    {
        Stop();
    }

    /* Initialisation des variables pour l'exécution. */
    Langue = LangageAutomate::FRANCAIS;
    RedefTempo = 0;
    en_cours = true;
    CheminReference = chemin_reference;
    config_chargee = false;
    animation_script = false;
    accepte_nav3d = true;
    id_compteur_fichier = 0;
    force_arret = false;
    pause = false;
    mesg_derniere_erreur.clear();

    if (Moteur)
    {
        Moteur->Reinit(code, false);
    }
    else
    {
        Moteur = new MoteurAutomate(code);
    }
    Calc.Reinit();

    if (execMultithread)
    {
        desactive_temporisation = true;
    }

#ifdef DEBUG_AUTOMATE
    qDebug() << "Exécution d'un script avec comme répertoire de référence :" << chemin_reference;
#endif // DEBUG_AUTOMATE

    if (!desactive_temporisation)
    {
        ExecEntete(); /* Parcours l'entête (les variables d'environnement) sans temporisation. Si le script impose la désactivation de l'animation, il n'y aura pas de temporisation. */
        desactive_temporisation = !animation_script;
    }

    if (desactive_temporisation)
    {
        while(execCycle(false, false)) /* Exécution sans gestion de la temporisation. */
        {
        }
    }
    else
    {
        Tempo.start(TempsSommeilTempo); // C'est la temporisation qui exécutera séquentiellement le code.

//        qDebug() << "Automate::exec :: Id tempo : " << Tempo.timerId();
        if (!Tempo.isActive()) /* Erreur lors du lancement du tempo ! */
        {
            ;
        }

        /* Attend la fin de l'exécution: */
        while(Tempo.isActive())
        {
            if (force_arret) /* Garde-fou */
            {
                break;
            }
            SommeilTempoQt(150); // Contrôle la fin du script.
        }
    }

    en_cours = false;

    if (Moteur->CompteurErreurs() == 0)
    {
        emit FinScript(); /* Uniquement émis si il n'y a pas eu d'erreurs. */
        if (force_arret)
        {
#ifdef DEBUG_AUTOMATE
            qDebug() << "Interruption automate (manuelle)";
#endif // DEBUG_AUTOMATE
            return AUTOMATE_ARRET;
        }
        return AUTOMATE_OK;
    }

#ifdef DEBUG_AUTOMATE
    qDebug() << "Interruption automate. Nombre d'erreurs :" << Moteur->CompteurErreurs();
#endif // DEBUG_AUTOMATE

    return AUTOMATE_ERR;
}

bool Automate::ExecEntete()
/* Commence la lecture du fichier de script et lis l'enete (permet par exemple de déterminer si l'on est en mode animation ou non). */
{
    int cpt = 0;
    while(execCycle(false, true)) /* Exécution sans gestion de la temporisation. */
    {
        ++cpt;
    }
    if (cpt)
    {
        Moteur->decLigneCourante(); /* Reviens à la ligne précédente pour être sûr qu'elle soit lue. */
    }
    return cpt > 0;
}

void Automate::execTempo()
/* A ne pas lancer manuellement ! Le tempo s'en charge... */
{
    if (!pause)
    {
        if (!Moteur->ValideExec())
        {
            Stop();
        }
        else
        {
            if (RedefTempo != 0) /* Redéfinition du tempo le temps d'une boucle */
            {
                if (Tempo.interval() == RedefTempo)
                {
                    Tempo.setInterval(TempsSommeilTempo);
                    RedefTempo = 0;
                }
                else
                {
                    Tempo.setInterval(RedefTempo);
                }
            }
            else /* Attend la fin de l'exécution du délai imposé dans le script */
            {
                if (!execCycle(false, false))
                {
                    Stop();
                }
            }
        }
    }
}

void Automate::Message_calc(const QString &m)
/* La calculatrice demande l'affichage d'un message. */
{
    MessageFmt(m, -1, true);
}

bool Automate::execCycle(bool mode_interpreteur, bool uniquement_entete)
/* Exécution d'un cycle. Renvoi true tant que l'exécution se poursuit. si CompteurErreurs a été incrémenté, c'est qu'il y a eu des problèmes lors de l'exécution. */
{
    if (!Moteur->ValideExec())
    {
//        qDebug() << "Invalide l'exécution : " << Moteur->LigneCourante()+1;
        return false;
    }

    if (Moteur->TaillePile() > LangageAutomate::ProfondeurMaxExecBlocs)
    {
        MessageFmtErreur(tr("Profondeur d'exécution maximum atteinte"), Moteur->LigneCourante()+1);
#ifdef DEBUG_AUTOMATE
        qDebug() << "Erreur automate : Profondeur d'exécution atteinte";
#endif // DEBUG_AUTOMATE
        Moteur->IncCompteurErreurs();
        return false;
    }

    int id_ligne = Moteur->Exec();

    if (id_ligne < 0)
    {
        if (id_ligne == -1) /* Fin du scripts ?! */
        {
//            qDebug() << "Invalide l'exécution : " << Moteur->LigneCourante()+1;
        }
        else if (id_ligne == -2) /* Erreur interne. */
        {
            MessageFmtErreur(tr("Erreur interne."), Moteur->LigneCourante()+1);
        }
        else
        {
        }
        return false;
    }

//    qDebug() << "ExecCycle : " << id_ligne << " : " << Moteur->LigneCode(id_ligne);

    if (!execLigne(id_ligne, mode_interpreteur, uniquement_entete))
    {
        if (uniquement_entete)
        {
            return false; /* Pas de rapport d'erreur, ça veut dire soit qu'il n'y a pas de variables d'environnement ou qu'on arrive à la fin de l'entête. */
        }
        if (!force_arret)
        {
#ifdef DEBUG_AUTOMATE
            qDebug() << "Erreur automate : Erreur d'exécution de la ligne " << id_ligne << " : " << Moteur->LigneCode(id_ligne).simplified().replace("\r", "");
#endif // DEBUG_AUTOMATE
            Moteur->IncCompteurErreurs();
        }
        return false;
    }
    if (force_arret)
    {
        return false;
    }
    return true;
}

bool Automate::execLigne(int id_ligne, bool mode_interpreteur, bool uniquement_entete)
/* Exécution de la ligne donnée en argument. */
{
    if (!Moteur->ValideLigne(id_ligne))
    {
        return false;
    }
    if (force_arret || !en_cours)
    {
        return false;
    }
    emit ExecutionLigne(Moteur->LigneReeleFichier(id_ligne), animation_script);

    const QString ligne = Moteur->LigneCode(id_ligne).simplified().replace("\r", "");

//    qDebug() << "Automate::execLigne : " << id_ligne << " : " << Moteur->LigneCode(id_ligne);

    QString commande;
    QStringList arguments;

    QString argument_p;
    bool etat_guillemets = false;
    bool etat_tableau = false;
    bool commentaire = false;
    int sequence = 0;

    const int n_ligne = ligne.size();
    for(int i2=0; i2<n_ligne; ++i2)
    {
        if (sequence == 0) // Nul, début de ligne
        {
            if (ligne[i2] == ' ')
            {
                continue;
            }
            else if (ligne[i2] == LangageAutomate::StrCommentaire[0]) // Commentaire
            {
                commentaire = true;
                break;
            }
            else
            {
                sequence = 1;
                commande += ligne[i2];
            }
        }
        else if (sequence == 1) // Commande
        {
            if (ligne[i2] == ' ')
            {
                sequence = 2;
                continue;
            }
            commande += ligne[i2];
        }
        else if (sequence == 2) // Arguments
        {
            if (!etat_tableau && ligne[i2] == '[')
            {
                argument_p += '[';
                etat_tableau = true;
            }
            else if (ligne[i2] == '\"')
            {
                if (!etat_guillemets)
                {
                    etat_guillemets = true;
                }
                else
                {
                    etat_guillemets = false;
                }
                continue;
            }
            else if (ligne[i2] == ' ')
            {
                if (etat_guillemets || etat_tableau)
                {
                    argument_p += ' '; // Si une chaine est entre guillements, ont reste dans le même argument.
                }
                else
                {
//                    qDebug() << argument_p;
                    arguments += argument_p; // Ajout du nouvel argument
                    argument_p.clear();
                }
            }
            else if (ligne[i2] == LangageAutomate::StrCommentaire[0]) // Commentaire (milieu de ligne)
            {
                break;
            }
            else
            {
                if (etat_tableau && ligne[i2] == ']')
                {
                    etat_tableau = false;
                }
                argument_p += ligne[i2];
            }
        }
    }

    if (!argument_p.isEmpty())
    {
        arguments += argument_p;
    }

    if (commentaire || commande.isEmpty())
    {
        return true;
    }

//    qDebug() << "(Debug automate) Commande : " << commande << ", arguments: " << arguments;

    if (commande[0] == LangageAutomate::StrEnvironnement[0])
    {
        if (mode_interpreteur && !arguments.size())
        {
            return execEnvLigne(id_ligne, commande, QString(), mode_interpreteur);
        }
        else if (arguments.size() == 1)
        {
            return execEnvLigne(id_ligne, commande, arguments[0], mode_interpreteur);
        }
    }
    else
    {
        if (uniquement_entete)
        {
            return false;
        }
        return execCodeLigne(id_ligne, commande, arguments, mode_interpreteur);
    }
    return false;
}

bool Automate::execBloc(int id_ligne, const QString &nom_bloc, bool boucle)
/* Gère l'exécution d'un bloc de code. */
{
//    qDebug() << "Exécution du bloc de code : " << nom_bloc << ". Appel depuis la ligne " << id_ligne+1;
    if (execution_verbeuse)
    {
        MessageFmt(tr("Exécution du bloc") + " " + nom_bloc, -1, false);
    }

    if (!Moteur->ExecBloc(nom_bloc, boucle))
    {
        MessageFmtErreur(tr("Bloc introuvable") + " : "  + nom_bloc, id_ligne);
        return false;
    }
    return true;
}

bool Automate::execEnvLigne(int id_ligne, const QString &commande, const QString &argument, bool mode_interpreteur)
/* Exécution d'une ligne (spécifiquement une variable d'environnement. Si le mode interpréteur est actif, on affiche les variable au lieu de les assigner. */
{
#ifdef DEBUG_AUTOMATE
    qDebug() << "Automate::execEnvLigne : [" << id_ligne << "] :: " << commande << " : " << argument;
#endif // DEBUG_AUTOMATE

//    AjoutJournal(commande, QStringList(argument));

    int env_ligne = EnvironnementAutomate(commande);
    int var = -1;

    if (!argument.length() && !mode_interpreteur)
    {
        MessageFmt(tr("Attention, assignation invalide pour variable d'environnement") + " " + commande, id_ligne, false);
        return false;
    }

    const bool affiche_vars = (mode_interpreteur && argument.isEmpty());

    switch(env_ligne)
    {
        case LangageAutomate::ENV_LANGUE:
            if (affiche_vars)
            {
                AfficheVariable(commande, Langue == LangageAutomate::FRANCAIS ? EnvironnementId(LangageAutomate::VAR_ENV_LANGUE_FR) : EnvironnementId(LangageAutomate::VAR_ENV_LANGUE_EN));
                return true;
            }
            else
            {
                var = EnvironnementAutomate(argument);
                if (var != LangageAutomate::ENV_NUL)
                {
                    Langue = var;
                    emit NouvelleLangueEnv(Langue, mode_interpreteur); /* Signalement du changement de langue dans l'automate. */
                    return true;
                }
                else
                {
                    MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                    return true;
                }
            }
            break;

        case LangageAutomate::ENV_VERSION:
            if (affiche_vars)
            {
                AfficheVariable(commande, QString::number(LangageAutomate::Version));
                return true;
            }
            else
            {
                if (AssigneInt(var, argument))
                {
                    if (var != LangageAutomate::ENV_NUL)
                    {
                        if (var > LangageAutomate::Version)
                        {
                            MessageFmt(tr("Attention, exécution d'un script plus récent que l'interpréteur."), -1, false);
                            return true;
                        }
                        else
                        {
                            return true; /* Les versions correspondent. */
                        }
                    }
                    else
                    {
                        MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                        return true;
                    }
                }
                else
                {
                    MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                    return true;
                }
            }
            break;

        case LangageAutomate::ENV_NAVIGATION:
            if (affiche_vars)
            {
                AfficheVariable(commande, QString::number(accepte_nav3d));
                return true;
            }
            else
            {
                if (AssigneInt(var, argument))
                {
                    accepte_nav3d = (var == 1);
                    return true;
                }
                else
                {
                    MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                    return true;
                }
            }
                break;

        case LangageAutomate::ENV_ANIMATION:
            if (affiche_vars)
            {
                AfficheVariable(commande, QString::number(animation_script));
                return true;
            }
            else
            {
                if (AssigneInt(var, argument))
                {
                    animation_script = (var == 1);
                    execution_verbeuse = animation_script;
                    return true;
                }
                else
                {
                    MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                    return true;
                }
            }
            break;

        case LangageAutomate::ENV_INCLURE_FICHIER:
            if (affiche_vars)
            {
                return false;
            }
            else
            {
                if (InclureFichier(id_ligne, argument))
                {
                    return true;
                }
                else
                {
                    MessageFmt(tr("Erreur lors de l'inclusion du fichier") + " \'" + argument + "\'", id_ligne, false);
                    return false;
                }
            }
            break;

        case LangageAutomate::ENV_COMPTEUR_FICHIER:
            if (affiche_vars)
            {
                AfficheVariable(commande, QString::number(id_compteur_fichier));
                return true;
            }
            else
            {
                if (AssigneInt(var, argument))
                {
                    id_compteur_fichier = var;
                    return true;
                }
                else
                {
                    MessageFmt(tr("Attention, erreur d'assignation de variable d'environnement") + " " + commande, id_ligne, false);
                    return true;
                }
            }
            break;

        default:
            MessageFmtErreur(tr("Variable d'environnement inconnue") + ": " + commande, id_ligne);
            break;
    }
    return false;
}

bool Automate::execCodeLigne(int id_ligne, const QString &commande, const QStringList &arguments, bool mode_interpreteur)
{
#ifdef DEBUG_AUTOMATE
    qDebug() << "Automate::execCodeLigne : [" << id_ligne+1 << "] :: " << commande << " : " << arguments;
#endif // DEBUG_AUTOMATE

    /* Affichage dans l'interface de chaque ligne en cours d'excution: */
//    AjoutJournal(commande, arguments);

    int commande_ligne = CommandeAutomate(commande, LangageAutomate::CONTEXTE_CMD);

    switch(commande_ligne)
    {
        case LangageAutomate::CMD_DECORATION:
            break;

        case LangageAutomate::CMD_ARRET:
            return false;

        case LangageAutomate::CMD_QUITTE:

            if (!Moteur->QuitteBloc()) /* Si un bloc est en cours d'exécution, il sera arrêté. */
            {
                Stop(false); // Sinon on quitte complètement l'exécution du script (ça n'est pas une erreur contrairement à CMD_ARRET).
            }
            return true;

        case LangageAutomate::TRACE_GROUPE:
            emit TraceGroupe();
            break;

        case LangageAutomate::TRACE_COULEUR:
            if (arguments.size() == 3)
            {
                float args[3] = { 1.0, 1.0, 1.0 };
                if (AssigneCoords(arguments, args, 0, 3))
                {
//                    couleur_trace.defRVBA(static_cast<Perspective3D::Poctet_t>(args[0]*255.0),
//                            static_cast<Perspective3D::Poctet_t>(args[1]*255.0),
//                            static_cast<Perspective3D::Poctet_t>(args[2]*255.0),
//                            255);

                    couleur_trace = qRgba(static_cast<unsigned char>(args[0]*255.0),
                            static_cast<unsigned char>(args[1]*255.0),
                            static_cast<unsigned char>(args[2]*255.0),
                            255);
                }
            }
            else
            {
                if (mode_interpreteur)
                {
//                    AfficheVariable(commande, QString::number(couleur_trace.R()) + " " + QString::number(couleur_trace.V()) + " " + QString::number(couleur_trace.B()));
                    AfficheVariable(commande, QString::number(qRed(couleur_trace)) + " " + QString::number(qGreen(couleur_trace)) + " " + QString::number(qBlue(couleur_trace)));
                }
                else
                {
                    MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("couleur"), QString::number(arguments.size()), QString::number(3)), id_ligne);
                }
            }
            break;

        case LangageAutomate::TRACE_LIGNE:
            if (arguments.size() == 4)
            {
                float coords[4] = {0., 0., 0., 0.};

                if (AssigneCoords(arguments, coords, 0, 4))
                {
                    emit TraceLigne(coords[0], coords[1], coords[2], coords[3], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("ligne"), QString::number(arguments.size()), QString::number(4)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_LIGNE_REL:
            if (arguments.size() == 4)
            {
                float coords[4] = {0., 0., 0., 0.};

                if (AssigneCoords(arguments, coords, 0, 4))
                {
                    emit TraceLigne(coords[0], coords[1], coords[0]+coords[2], coords[1]+coords[3], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("ligne_rel"), QString::number(arguments.size()), QString::number(4)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_CERCLE:
            if (arguments.size() == 3)
            {
                float coords[3] = {0., 0., 0.};
                if (AssigneCoords(arguments, coords, 0, 3))
                {
                    emit TraceCourbe(coords[0], coords[1], coords[2], coords[2], 0, 360, 0, couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("cercle"), QString::number(arguments.size()), QString::number(3)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_ELLIPSE:
            if (arguments.size() == 5)
            {
                float coords[5] = {0., 0., 0., 0., 0.};
                if (AssigneCoords(arguments, coords, 0, 5))
                {
                    emit TraceCourbe(coords[0], coords[1], coords[2], coords[3], 0, 360, coords[4], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("ellipse"), QString::number(arguments.size()), QString::number(5)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_COURBE:
            if (arguments.size() == 7)
            {
                float coords[7] = {0., 0., 0., 0., 0., 0., 0.};
                if (AssigneCoords(arguments, coords, 0, 7))
                {
                    emit TraceCourbe(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], coords[6], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("courbe"), QString::number(arguments.size()), QString::number(7)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_RECT:
            if (arguments.size() == 4)
            {
                float coords[4] = {0., 0., 0., 0.};
                if (AssigneCoords(arguments, coords, 0, 4))
                {
                    const Perspective3D::Ppoint2D p1(coords[0], coords[1]);
                    const Perspective3D::Ppoint2D p2(coords[0]+coords[2], coords[1]+coords[3]);
                    emit TraceLigne(p1.X(), p1.Y(), p2.X(), p1.Y(), couleur_trace);
                    emit TraceLigne(p2.X(), p1.Y(), p2.X(), p2.Y(), couleur_trace);
                    emit TraceLigne(p2.X(), p2.Y(), p1.X(), p2.Y(), couleur_trace);
                    emit TraceLigne(p1.X(), p2.Y(), p1.X(), p1.Y(), couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("rect"), QString::number(arguments.size()), QString::number(4)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_TEXTE:
            if (arguments.size() == 5)
            {
                const QString &str = arguments[0];
                float coords[4] = {0., 0., 0., 0.};
                if (AssigneCoords(arguments, coords, 1, 4))
                {
                    emit TraceTexte(str, coords[0], coords[1], coords[2], coords[3], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("texte"), QString::number(arguments.size()), QString::number(5)), id_ligne);
            }
            break;

        case LangageAutomate::TRACE_POINT:
            if (arguments.size() == 2)
            {
                float coords[2] = {0., 0.};
                if (AssigneCoords(arguments, coords, 0, 2))
                {
                    emit TracePoint(coords[0], coords[1], couleur_trace);
                }
                else
                {
                    MessageFmtErreur(tr("Erreur lors de l'assignation des valeurs"), id_ligne);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect (entité %1, %2/%3 requis)").arg(tr("point"), QString::number(arguments.size()), QString::number(2)), id_ligne);
            }
            break;

        case LangageAutomate::CMD_MESSAGE:
        {
            QString mesg = FormateArgumentsMessage(arguments);
            if (!mesg.isEmpty())
            {
                MessageFmt(FormateMessageUtilisateur(mesg), -1, true);
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument minimum."), id_ligne);
            }
            break;
        }

        case LangageAutomate::CMD_DICTE:
        {
//#ifdef SUPPORT_DICTION
            QString mesg = FormateArgumentsMessage(arguments, true);
            if (!mesg.isEmpty())
            {
                mesg = FormateMessageUtilisateur(mesg);

                MessageFmt(mesg, -1, false); /* Note en parallèle de la diction */
                emit Dicte(mesg, LangueDiction());
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument minimum."), id_ligne);
            }
//#endif // SUPPORT_DICTION
            break;
        }

        case LangageAutomate::CMD_NOTE:
        {
            QString mesg = FormateArgumentsMessage(arguments);
            if (!mesg.isEmpty())
            {
                MessageFmt(FormateMessageUtilisateur(mesg), -1, false);
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument requis."), id_ligne);
            }
            break;
        }

        case LangageAutomate::CMD_CONFIRMATION:
            if (arguments.size() == 1)
            {
                QString copie = arguments[0];
                emit Confirmation(FormateMessageUtilisateur(copie));
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument requis."), id_ligne);
            }
            break;

        case LangageAutomate::CMD_CADRAGE:
            emit CadrageVue(animation_script);
            break;

        case LangageAutomate::CMD_REINIT:
            emit(ReinitInterface(AccepteNavigation3D() ? false : true));
            break;

        case LangageAutomate::CMD_REINIT_VUES:
            emit(ReinitInterface(true));
            break;

        case LangageAutomate::CMD_OUVRIR:
            if (arguments.size())
            {
                emit OuvrirFichier(CheminReference + QDir::separator() + arguments[0]);
            }
            else
            {
                MessageFmtErreur(tr("Demande d'ouverture d'un fichier sans argument. Fin du script."), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_FERMER2D:
            emit FermeFichier();
            break;

        case LangageAutomate::CMD_ZONE2D:
            emit Onglet2D();
            break;

        case LangageAutomate::CMD_ONGLET3D:
            if (arguments.size() == 1)
            {
                unsigned int id_3d = 0;
                if (AssigneUInt(id_3d, arguments[0]))
                {
                    if (AccepteNavigation3D())
                        emit Onglet3D(id_3d);
                }
                else
                {
                    MessageFmtErreur(tr("Impossible de convertir le nombre"), id_ligne);
                    return false;
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument requis."), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_QUESTION:
            if (arguments.size() == 4)
            {
                const int type_valeur = CommandeAutomate(arguments[0], LangageAutomate::CONTEXTE_ARGUMENT);
                if (type_valeur != LangageAutomate::CMD_NUL)
                {
                    bool annuler_exec = false;
                    const QString &nom_variable = arguments[1];
                    const QString &question = arguments[2];
                    if (type_valeur == LangageAutomate::ARG_QUESTION_BOOL)
                    {
                        bool valeur_defaut = true;
                        if (!AssigneBool(valeur_defaut, arguments[3]))
                        {
                            MessageFmtErreur(tr("Erreur lors de la conversion booléenne"), id_ligne);
                            return false;
                        }
                        bool resultat = valeur_defaut;
                        emit Question_Bool(question, valeur_defaut, &resultat, &annuler_exec);
                        if (!Calc.AjoutVariableBool(nom_variable, resultat))
                        {
                            MessageFmtErreur(tr("Erreur lors de l'assignation de la variable (bool)"), id_ligne);
                            return false;
                        }
                        if (annuler_exec)
                        {
                            Stop(false);
                        }
                    }
                    else if (type_valeur == LangageAutomate::ARG_QUESTION_INT)
                    {
                        int valeur_defaut = 0;
                        if (!AssigneInt(valeur_defaut, arguments[3]))
                        {
                            MessageFmtErreur(tr("Erreur lors de la conversion entière"), id_ligne);
                            return false;
                        }
                        int resultat = valeur_defaut;
                        emit Question_Int(question, valeur_defaut, &resultat, &annuler_exec);
                        if (!Calc.AjoutVariableInt(nom_variable, resultat))
                        {
                            MessageFmtErreur(tr("Erreur lors de l'assignation de la variable (int)"), id_ligne);
                            return false;
                        }
                        if (annuler_exec)
                        {
                            Stop(false);
                        }
                    }
                    else if (type_valeur == LangageAutomate::ARG_QUESTION_FLOAT)
                    {
                        double valeur_defaut = 0;
                        if (!AssigneDouble(valeur_defaut, arguments[3]))
                        {
                            MessageFmtErreur(tr("Erreur lors de la conversion décimale"), id_ligne);
                            return false;
                        }
                        double resultat = valeur_defaut;
                        emit Question_Float(question, valeur_defaut, &resultat, &annuler_exec);
                        if (!Calc.AjoutVariableFloat(nom_variable, resultat))
                        {
                            MessageFmtErreur(tr("Erreur lors de l'assignation de la variable (float)"), id_ligne);
                            return false;
                        }
                        if (annuler_exec)
                        {
                            Stop(false);
                        }
                    }
                    else
                    {
                        MessageFmtErreur(tr("Type incorrect"), id_ligne);
                        return false;
                    }
                }
                else
                {
                    MessageFmtErreur(tr("Type incorrect"), id_ligne);
                    return false;
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_VUE:
            if (arguments.size() == 6)
            {
                const int cmd_vue = CommandeAutomate(arguments[0], LangageAutomate::CONTEXTE_ARGUMENT);
                if (cmd_vue != LangageAutomate::CMD_NUL)
                {
                    Perspective3D::vues2D_t vue = ((cmd_vue == LangageAutomate::ARG_VUEFACE) ? Perspective3D::vues2D_t::VUEFACE :
                                                  ((cmd_vue == LangageAutomate::ARG_VUECOTE) ? Perspective3D::vues2D_t::VUECOTE :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEHAUT) ? Perspective3D::vues2D_t::VUEHAUT :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEMULT) ? Perspective3D::vues2D_t::VUEMULT :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEEXTRUSION) ? VUE_EXTRUSION :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEREVOLUTION) ? VUE_REVOLUTION :
                                                                                       Perspective3D::vues2D_t::VUERREUR))))));

                    if (vue != Perspective3D::vues2D_t::VUERREUR && vue != Perspective3D::vues2D_t::VUENUL)
                    {
                        bool absolu = (CommandeAutomate(arguments[1], LangageAutomate::CONTEXTE_ARGUMENT) == LangageAutomate::ARG_ABSOLU);

                        float coords[4] = {0., 0., 0., 0.};

                        if (AssigneCoords(arguments, coords, 2, 4))
                        {
                            if (absolu) /* Si les coordonnées ont été définis commes absolues, on passe en coordonnées relatives. */
                            {
                                coords[2] = coords[2] - coords[0];
                                coords[3] = coords[3] - coords[1];
                            }
                            QRectF rect(coords[0], coords[1], coords[2], coords[3]);
                            emit DefVue(vue, rect);
                        }
                        else
                        {
                            MessageFmtErreur(tr("Erreur lors de la conversion décimale"), id_ligne);
                            return false;
                        }
                    }
                    else
                    {
                        MessageFmtErreur(tr("Vue incorrecte"), id_ligne);
                        return false;
                    }
                }
                else
                {
                    MessageFmtErreur(tr("Vue incorrecte"), id_ligne);
                    return false;
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_ORIGINE:
            if (arguments.size() == 3)
            {
                int cmd_vue = CommandeAutomate(arguments[0], LangageAutomate::CONTEXTE_ARGUMENT);
                if (cmd_vue != LangageAutomate::CMD_NUL)
                {
                    Perspective3D::vues2D_t vue = ((cmd_vue == LangageAutomate::ARG_VUEFACE) ? Perspective3D::vues2D_t::VUEFACE :
                                                  ((cmd_vue == LangageAutomate::ARG_VUECOTE) ? Perspective3D::vues2D_t::VUECOTE :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEHAUT) ? Perspective3D::vues2D_t::VUEHAUT :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEEXTRUSION) ? VUE_EXTRUSION :
                                                  ((cmd_vue == LangageAutomate::ARG_VUEREVOLUTION) ? VUE_REVOLUTION :
                                                                                       Perspective3D::vues2D_t::VUERREUR)))));

                    if (vue != Perspective3D::vues2D_t::VUERREUR)
                    {
                        float coords[2] = {0., 0.};

                        if (AssigneCoords(arguments, coords, 1, 2))
                        {
                            QPointF point(coords[0], coords[1]);
                            emit DefOrigine(vue, point);
                        }
                        else
                        {
                            MessageFmtErreur(tr("Erreur lors de la conversion décimale"), id_ligne);
                            return false;
                        }
                    }
                    else
                    {
                        MessageFmtErreur(tr("Vue incorrecte"), id_ligne);
                        return false;
                    }
                }
                else
                {
                    MessageFmtErreur(tr("Vue incorrecte"), id_ligne);
                    return false;
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_GEN3D:
            if (arguments.size() == 1)
            {
                qreal param = -1;
                AssigneDouble(param, arguments[0]);
                emit Generation(parametres_p3d, true, param);
            }
            else
            {
                emit Generation(parametres_p3d, true, -1);
            }
            break;

        case LangageAutomate::CMD_EXPORT3D:
            if (AccepteExport3D())
            {
                if (arguments.size() == 1)
                {
                    emit Export3D(arguments[0]);
                }
                else
                {
                    MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                    return false;
                }
            }
            else
            {
                MessageFmtErreur(tr("Commande d'export 3D refusée dans la configuration actuelle"), id_ligne);
//                return false;
            }
            break;

        case LangageAutomate::CMD_GEN3D_BLOQUANT:
            Pause();

            if (arguments.size() == 1)
            {
                qreal param = -1;
                AssigneDouble(param, arguments[0]);
                emit Generation(parametres_p3d, false, param);
            }
            else
            {
                emit Generation(parametres_p3d, false, -1);
            }
            break;

        case LangageAutomate::CMD_GEN3D_NON_BLOQUANT:
            if (arguments.size() == -1)
            {
                qreal param = -1;
                AssigneDouble(param, arguments[0]);
                emit Generation(parametres_p3d, false, param);
            }
            else
            {
                emit Generation(parametres_p3d, false, -1);
            }
            break;

        case LangageAutomate::CMD_ANIME_3D:
            emit Anime3D();
            break;

        case LangageAutomate::CMD_FERMER3D:
            if (AccepteNavigation3D())
            {
                emit FermeGeneration();
            }
            break;

        case LangageAutomate::CMD_DELAI:
            if (arguments.size())
            {
                RedefTempo = Locale.toInt(arguments[0]);
            }
            else
            {
                RedefTempo = 1000;
            }
            break;

        case LangageAutomate::CMD_DEF_BLOC: /* Définition d'un bloc de code, on le parcourt sans l'exécuter */
            if (arguments.size() == 1)
            {
                BlocCodeAutomate b(arguments[0]);

                int id_ligne_tmp = id_ligne + 1;

                while (true)
                {
                    if (!Moteur->ValideLigne(id_ligne_tmp))
                    {
                        MessageFmtErreur(tr("Fin de fichier atteinte lors de l'analyse d'un bloc. Est il fermé ?"), id_ligne);
                        return false;
                    }

                    QStringRef mot = extrait_mot(Moteur->LigneCode(id_ligne_tmp));

                    if (mot == LangageAutomate::OuvertureBlocInstruction) /* Recherche de l'ouverture du bloc. */
                    {
                        if (!b.ValideLigneDebut()) //  b.LigneDebut() == -1
                        {
                            b.defLigneDebut(id_ligne_tmp+1);
                        }
                        else
                        {
                            MessageFmtErreur(tr("Double ouverture de bloc"), id_ligne);
                            return false;
                        }
                    }
                    else if (mot == LangageAutomate::FermetureBlocInstruction) /* Recherche de la cloture du bloc. */
                    {
                        if (b.ValideLigneDebut())
                        {
                            b.defLigneFin(id_ligne_tmp-1);
                            break;
                        }
                        else
                        {
                            MessageFmtErreur(tr("Fermeture d'un bloc non ouvert"), id_ligne);
                            return false;
                        }
                    }
                    ++id_ligne_tmp;
                }

                if (!b.ValideLigneDebut() || !b.ValideLigneFin() || !b.ValideNom())
                {
                    MessageFmtErreur(tr("Erreur indéfinie lors de l'analyse d'un bloc"), id_ligne);
                    return false;
                }
                else
                {
#ifdef DEBUG_AUTOMATE
                    qDebug() << "Définition du bloc " << b.Nom() << " :: " << b.LigneDebut()+1 << " -> " << b.LigneFin()+1;
#endif // DEBUG_AUTOMATE
                    Moteur->AjoutBloc(b);
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument requis."), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_EXEC_BLOC:
#ifdef DEBUG_AUTOMATE
            qDebug() << "Exécution de bloc ?: " << commande << " : " << arguments << " (" << id_ligne << ")";
#endif // DEBUG_AUTOMATE
            if (arguments.size() == 1)
            {
                if (!execBloc(id_ligne, arguments[0]))
                {
                    return false;
                }
            }
            else
            {
                if (mode_interpreteur)
                {
                    /* Affichage de la liste des blocs */
                    const QVector<BlocCodeAutomate> &blocs = Moteur->BlocsCodes();
                    if (blocs.size())
                    {
                        QString str_liste_blocs;
                        for(int ix=0; ix<blocs.size(); ++ix)
                        {
                            const QString &n_bloc = blocs[ix].Nom();
                            if (ix)
                            {
                                str_liste_blocs += " ";
                            }
                            str_liste_blocs += n_bloc;
                        }
                        MessageFmt(tr("Blocs exécutables (%1) : \"%2\".").arg(QString::number(blocs.size()), str_liste_blocs), -1);
                    }
                    else
                    {
                        MessageFmt(tr("Aucun bloc défini."), -1);
                    }
                }
                else
                {
                    MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument requis."), id_ligne);
                    return false;
                }
            }
            break;

        case LangageAutomate::CMD_VAR_CALC:
        case LangageAutomate::CMD_EXEC_CALC:
            if (arguments.size() != 0)
            {
                for(int i=0; i<arguments.size(); ++i)
                {
                    if (!Calc.ExecCode(arguments[i]))
                    {
                        MessageFmtErreur(tr("Erreur lors du traitement de l'expression") + " " + arguments[i], id_ligne);
                        return false;
                    }
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect. Un argument minimum."), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_BOUCLE_CALC:
            if (arguments.size() == 2)
            {
                if (Calc.ExecCond(arguments[0], false))
                {
                    if (!execBloc(id_ligne, arguments[1], true)) /* Créera une boucle tant que la condition n'est pas remplie */
                    {
                        return false;
                    }
                }
                else /* Fin de boucle... */
                {
                    Moteur->FinBoucle();
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_COND_CALC:
            if (arguments.size() == 2 || arguments.size() == 3)
            {
                if (Calc.ExecCond(arguments[0], false))
                {
                    if (!execBloc(id_ligne, arguments[1]))
                    {
                        return false;
                    }
                }
                else
                {
                    if (arguments.size() == 3) /* Si un bloc de sortie a été défini... */
                    {
                        if (!execBloc(id_ligne, arguments[2]))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                return false;
            }
            break;

        case LangageAutomate::CMD_CHARGE_CONFIG:
            if (!ChargeConfig())
            {
                MessageFmtErreur(tr("Erreur lors du chargement de la configuration de Zetta6"), -1);
                return false;
            }
            break;

        case LangageAutomate::CMD_FORCE_CONFIG:
            if (config_chargee)
            {
                emit ForceConfig(parametres_p3d);
            }
            else
            {
                MessageFmtErreur(tr("Erreur, tentative de réécriture de la configuration alors qu'elle n'a pas été chargée"), -1);
            }
            break;

        case LangageAutomate::CMD_PARAM:
            if (config_chargee)
            {
                if (arguments.size() == 2)
                {
//                    union u_conv { int i ; float f; double d; } conv;

                    int type_param = CommandeAutomate(arguments[0], LangageAutomate::CONTEXTE_PARAM);
                    unsigned int val_param_enum=0;

                    switch (type_param)
                    {
                        case LangageAutomate::PARAM_TOLERANCE:
                            AssignePfloat(parametres_p3d.Tolerance, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_DIAGO_ECHELLE_UNIFORME:
                            AssigneUInt(parametres_p3d.TailleDiagoUniforme, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_ARRONDI:
                            AssigneInt(parametres_p3d.Arrondi_dessin, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_DIVIS_COURBES:
                            AssigneInt(parametres_p3d.Divisionscercles, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_PROJS:
                            AssigneUInt(val_param_enum, arguments[1]);
                            parametres_p3d.Projections = static_cast<Perspective3D::vues2D_t>(val_param_enum);
                            break;
                        case LangageAutomate::PARAM_INFOS:
                            AssigneUInt(val_param_enum, arguments[1]);
                            parametres_p3d.Informations = static_cast<Perspective3D::infos3d_t>(val_param_enum);
                            break;
                        case LangageAutomate::PARAM_TAILLE_TEXTE_3D:
                            AssigneInt(parametres_p3d.Tailletexte3d, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_FILAIRE:
                            AssigneBool(parametres_p3d.Filaire_uniquement, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_MULTI_THREADING:
                            AssigneBool(parametres_p3d.MultiThreading, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_PARAMS3D:
                            AssigneUInt(val_param_enum, arguments[1]);
                            parametres_p3d.Parametres3d = static_cast<Perspective3D::params_gen3d_t>(val_param_enum);
                            break;
                        case LangageAutomate::PARAM_TAILLE_MAX_SURFACES:
                            AssigneUInt(parametres_p3d.Taille_surfaces, arguments[1]);
                            break;
                        case LangageAutomate::PARAM_NORME_VUES:
                            AssigneUInt(val_param_enum, arguments[1]);
                            parametres_p3d.Norme_vues = static_cast<Perspective3D::normevues_t>(val_param_enum);
                            break; // !
                        case LangageAutomate::PARAM_ID_RAL:
                            AssigneUInt(parametres_p3d.Id_ral, arguments[1]);
                            break;
                        default:
                            MessageFmt(tr("Paramètre inconnu"), id_ligne);
                            break;
                    }
                }
                else
                {
                    if (mode_interpreteur && arguments.size() == 1)
                    {
                        int type_param = CommandeAutomate(arguments[0], LangageAutomate::CONTEXTE_PARAM);
                        switch (type_param)
                        {
                            case LangageAutomate::PARAM_TOLERANCE:
                                AfficheVariable(commande, QString::number(parametres_p3d.Tolerance));
                                break;
                            case LangageAutomate::PARAM_DIAGO_ECHELLE_UNIFORME:
                                AfficheVariable(commande, QString::number(parametres_p3d.TailleDiagoUniforme));
                                break;
                            case LangageAutomate::PARAM_ARRONDI:
                                AfficheVariable(commande, QString::number(parametres_p3d.Arrondi_dessin));
                                break;
                            case LangageAutomate::PARAM_DIVIS_COURBES:
                                AfficheVariable(commande, QString::number(parametres_p3d.Divisionscercles));
                                break;
                            case LangageAutomate::PARAM_PROJS:
                                AfficheVariable(commande, QString::number(static_cast<unsigned int >(parametres_p3d.Projections)));
                                break;
                            case LangageAutomate::PARAM_INFOS:
                                AfficheVariable(commande, QString::number(static_cast<unsigned int >(parametres_p3d.Informations)));
                                break;
                            case LangageAutomate::PARAM_TAILLE_TEXTE_3D:
                                AfficheVariable(commande, QString::number(parametres_p3d.Tailletexte3d));
                                break;
                            case LangageAutomate::PARAM_FILAIRE:
                                AfficheVariable(commande, QString::number(parametres_p3d.Filaire_uniquement));
                                break;
                            case LangageAutomate::PARAM_MULTI_THREADING:
                                AfficheVariable(commande, QString::number(parametres_p3d.MultiThreading));
                                break;
                            case LangageAutomate::PARAM_PARAMS3D:
                                AfficheVariable(commande, QString::number(static_cast<unsigned int >(parametres_p3d.Parametres3d)));
                                break;
                            case LangageAutomate::PARAM_TAILLE_MAX_SURFACES:
                                AfficheVariable(commande, QString::number(parametres_p3d.Taille_surfaces));
                                break;
                            case LangageAutomate::PARAM_NORME_VUES:
                                AfficheVariable(commande, QString::number(static_cast<unsigned int >(parametres_p3d.Norme_vues)));
                                break; // !
                            case LangageAutomate::PARAM_ID_RAL:
                                AfficheVariable(commande, QString::number(parametres_p3d.Id_ral));
                                break;
                            default:
                                MessageFmt(tr("Paramètre inconnu"), id_ligne);
                                break;
                        }
                    }
                    else
                    {
                        MessageFmtErreur(tr("Nombre d'arguments incorrect"), id_ligne);
                        return false;
                    }
                }
            }
            else
            {
                MessageFmtErreur(tr("Tentative de changer un paramètre sans avoir appelé la commande de chargement"), id_ligne);
                return false;
            }
            break;

        default:
            MessageFmtErreur(tr("Commande inconnue") + ": " + commande, id_ligne);
            return false;
    }
    return true;
}

Th_Automate::Th_Automate(const QString &chemin_zetta6) : automate(chemin_zetta6), fichier_exec(""), resultat_exec(AUTOMATE_ERR)
{
    ;
}

Th_Automate::~Th_Automate()
{
}

void Th_Automate::defFichierExec(const QString &fichier)
{
    fichier_exec = fichier;
    resultat_exec = AUTOMATE_ERR;
}

int Th_Automate::execFichier(bool multi_thread)
{
    if (automate.ExecEnCours())
    {
        return AUTOMATE_ERR;
    }
    automate.defExecMultiThread(multi_thread);
    return automate.execFichier(fichier_exec, false);
}

void Th_Automate::run()
{
    resultat_exec = execFichier(true);
}

Interpreteur::Interpreteur(const QString &chemin_reference, int langue) : Automate(chemin_reference)
{
    Langue = langue;
    RedefTempo = 0;
    CheminReference = chemin_reference;
    config_chargee = false;
    animation_script = false;
    accepte_nav3d = true;
    id_compteur_fichier = 0;
}

Interpreteur::~Interpreteur()
{
    ;
}

bool Interpreteur::defLangue(int langue)
{
    Langue = langue;
    return true;
}

bool Interpreteur::defConfig(Parametres *config)
{
    Automate::defConfig(config);
    accepte_export3d = false;
//     return true;
   return ChargeConfig();
}

bool Interpreteur::ListeCommandes(QStringList &liste) const
/* Génère la liste des commandes acceptées par l'interpréteur pour servir à générer une liste d'auto-complétion. */
{
    using namespace LangageAutomate;
    liste.clear();

#define INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE, COMMANDE) { const char *cmd_##COMMANDE = CommandeId(LangageAutomate::CONTEXTE, LangageAutomate::COMMANDE, Langue);\
    if (cmd_##COMMANDE) { liste.append(QString(cmd_##COMMANDE)); } }

#define INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE1, COMMANDE1, CONTEXTE2, COMMANDE2) \
    {\
        const char *cmd_##COMMANDE1 = CommandeId(LangageAutomate::CONTEXTE1, LangageAutomate::COMMANDE1, Langue);\
        if (cmd_##COMMANDE1)\
        {\
            const char *cmd_##COMMANDE2 = CommandeId(LangageAutomate::CONTEXTE2, LangageAutomate::COMMANDE2, Langue);\
            if (cmd_##COMMANDE2)\
            {\
                liste.append(QString(cmd_##COMMANDE1) + " " + cmd_##COMMANDE2);\
            }\
        }\
    }

#define INTERPRETEUR_AJOUT_ENV_LISTE(ENV1, ENV2) { const char *cmd_##ENV1 = EnvironnementId(LangageAutomate::ENV1);\
    if (cmd_##ENV1)\
    {\
        if (ENV2 >= 0)\
        {\
            const char *cmd_##ENV2 = EnvironnementId(ENV2);\
            if (cmd_##ENV2)\
            {\
                liste.append(QString(cmd_##ENV1) + " " + cmd_##ENV2);\
            }\
            else\
            {\
                liste.append(QString(cmd_##ENV1));\
            }\
        }\
        else\
        {\
            liste.append(QString(cmd_##ENV1));\
        }\
    } }

    liste.append(LangageAutomate::StrCommentaire);
    const int id_invalide = -1;

    INTERPRETEUR_AJOUT_ENV_LISTE(ENV_LANGUE, VAR_ENV_LANGUE_FR);
    INTERPRETEUR_AJOUT_ENV_LISTE(ENV_LANGUE, VAR_ENV_LANGUE_EN);
    INTERPRETEUR_AJOUT_ENV_LISTE(ENV_VERSION, id_invalide);
    INTERPRETEUR_AJOUT_ENV_LISTE(ENV_ANIMATION, id_invalide);
//    INTERPRETEUR_AJOUT_ENV_LISTE(ENV_NAVIGATION, id_invalide);

    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_DICTE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_MESSAGE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_NOTE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_CADRAGE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_REINIT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_REINIT_VUES);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_OUVRIR);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_FERMER2D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_ZONE2D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_ONGLET3D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_VUE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUEFACE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUECOTE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUEHAUT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUEEXTRUSION);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUEREVOLUTION);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_VUE, CONTEXTE_ARGUMENT, ARG_VUEMULT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_ORIGINE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUEFACE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUECOTE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUEHAUT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUEEXTRUSION);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUEREVOLUTION);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_ORIGINE, CONTEXTE_ARGUMENT, ARG_VUEMULT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_GEN3D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_CHARGE_CONFIG);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_FORCE_CONFIG);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_PARAM);

    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_TOLERANCE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_ARRONDI);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_DIVIS_COURBES);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_PROJS);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_INFOS);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_TAILLE_TEXTE_3D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_FILAIRE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_MULTI_THREADING);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_PARAMS3D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_TAILLE_MAX_SURFACES);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_NORME_VUES);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_ID_RAL);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE2(CONTEXTE_CMD, CMD_PARAM, CONTEXTE_PARAM, PARAM_DIAGO_ECHELLE_UNIFORME);

    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_FERMER3D);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_DEF_BLOC);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_EXEC_BLOC);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_VAR_CALC);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_EXEC_CALC);
//    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_BOUCLE_CALC);
//    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_CMD, CMD_COND_CALC);

    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_GROUPE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_COULEUR);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_LIGNE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_LIGNE_REL);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_CERCLE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_ELLIPSE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_COURBE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_RECT);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_TEXTE);
    INTERPRETEUR_AJOUT_COMMANDE_LISTE(CONTEXTE_TRACE, TRACE_POINT);

#undef INTERPRETEUR_AJOUT_COMMANDE_LISTE
#undef INTERPRETEUR_AJOUT_ENV_LISTE
#undef INTERPRETEUR_AJOUT_COMMANDE_LISTE2

    return (liste.size() > 0);
}

int Interpreteur::execCommande(const QString &cmd)
{
    if (cmd.isEmpty())
    {
        return AUTOMATE_OK;
    }

    en_cours = true;
    force_arret = false;
    pause = false;

    if (!Moteur)
    {
        Moteur = new MoteurAutomate(cmd);
    }
    else
    {
        Moteur->Reinit(cmd, true);
    }
//    Calc.Reinit();

    while(execCycle(true, false)) /* Exécution sans gestion de la temporisation. */
    {
        ;
    }

    en_cours = false;

    if (Moteur->CompteurErreurs() == 0)
    {
        if (force_arret)
        {
#ifdef DEBUG_AUTOMATE
            qDebug() << "Interruption interpréteur (manuelle)";
#endif // DEBUG_AUTOMATE
            return AUTOMATE_ARRET;
        }
        return AUTOMATE_OK;
    }

#ifdef DEBUG_AUTOMATE
    qDebug() << "Interruption interpréteur. Nombre d'erreurs :" << Moteur->CompteurErreurs();
#endif // DEBUG_AUTOMATE

//    const int id_ligne_desactive = Moteur->LigneCourante();
//    if (id_ligne_desactive)
//    {
//        Moteur->DesactiveLigneCode(id_ligne_desactive);
//        Moteur->decLigneCourante();
//    }

//    qDebug() << "Erreur de ligne lors de l'interprétation.";
//    qDebug() << "Liste des codes après désactivation de la ligne : (" << Moteur->ListeLignesCode().size() << ") :: " << Moteur->ListeLignesCode();
//    qDebug() << "Position courante dans le code : " << Moteur->LigneCourante();

    return AUTOMATE_ERR;
}

ScriptAutomate::ScriptAutomate(const QString &nom_fichier, int langue, bool animation, bool mode_ajout) :
    Animation(animation), ValideF(false), id_compteur_fichier(0)
{
    Fichier.setFileName(nom_fichier);
    profondeur_blocs = 0;

    if (mode_ajout) /* Mode ajout, réutilise les variables d'environnement existantes. */
    {
        Langue = VarEnvironnementScript(nom_fichier, EnvironnementIdAutomate(LangageAutomate::ENV_LANGUE));
        if (Langue.isEmpty())
        {
            Langue = EnvironnementIdAutomate(LangageAutomate::VAR_ENV_LANGUE_EN);
        }

        id_compteur_fichier = Locale.toInt(VarEnvironnementScript(nom_fichier, EnvironnementIdAutomate(LangageAutomate::ENV_COMPTEUR_FICHIER))) + 1;
        Animation = Locale.toInt(VarEnvironnementScript(nom_fichier, EnvironnementIdAutomate(LangageAutomate::ENV_ANIMATION)));

        /* Sauvegarde du corps de l'ancien fichier. */
        QByteArray ancien_script;
        if (Fichier.open(QFile::ReadOnly | QFile::Text))
        {
            ancien_script = Fichier.readAll();
            Fichier.close();
        }

        /* Ajout des nouvelles variables d'environnement. */
        if (Fichier.open(QFile::WriteOnly | QFile::Text))
        {
            if (AjoutEnvironnement(true))
            {
                ValideF = true;
            }
        }

        /* Ajout du corps de l'ancien fichier. */
        if (!ancien_script.isEmpty())
        {
            /* Vire l' entête des variables d'environnement de l'ancien script */

            int pos_debut_env = -1;
            int pos_fin_env = -1;
            const unsigned int n_ancien_script = ancien_script.size();

            for(unsigned int i=0; i<n_ancien_script; ++i)
            {
                if (ancien_script[i] == CHAR_ENV)
                {
                    if (pos_debut_env == -1)
                    {
                        pos_debut_env = i;
                    }

                    /* Va jusqu'à la fin de la ligne */
                    for(; i<n_ancien_script; ++i)
                    {
                        if (ancien_script[i] == '\n')
                        {
                            pos_fin_env = i+1;
                            break;
                        }
                    }
                }
                else if (pos_debut_env != -1 && pos_fin_env != -1)
                {
                    break;
                }
            }

            if (pos_debut_env != -1 && pos_fin_env != -1)
            {
                ancien_script.remove(pos_debut_env, (pos_fin_env-pos_debut_env)+1);
            }

            Fichier.write(ancien_script);
            Fichier.write("\n##############################################################\n\n");
        }
    }
    else
    {
        Langue = EnvironnementIdAutomate(langue);
        if (Fichier.open(QFile::WriteOnly | QFile::Text))
        {
            if (AjoutEnvironnement(false))
            {
                ValideF = true;
            }
        }
    }
}

ScriptAutomate::~ScriptAutomate()
{
    if (ValideF)
    {
        Fichier.close();
    }
}

QString ScriptAutomate::VarEnvironnementScript(const QString &chemin_script, const char *var) const
/* Renvoi la valeur d'une variable d'environnement sur le script (qui doit être existant). Renvoi une chaîne vide en cas d'erreur. */
{
    if (!var)
    {
        return QString();
    }

    const size_t n_str_env_compteur = strlen(var);

    QFile fichier;
    fichier.setFileName(chemin_script);

    if (!fichier.open(QFile::ReadOnly | QFile::Text))
    {
        return QString();
    }

    const unsigned int taille_tampon_ligne = 128;
    char tampon_ligne[taille_tampon_ligne];
    const unsigned int taille_tampon_id = taille_tampon_ligne-24;
    char tampon_val[taille_tampon_id];
    bool debut_env = false;

    QString ret;

    for(unsigned int i=0; i<10; ++i)
    {
        qint64 n = fichier.readLine(tampon_ligne, taille_tampon_ligne-1);
        if (n == -1)
            break;

        if (tampon_ligne[0] != CHAR_ENV)
        {
            if (debut_env)
                break;
            else
                continue;
        }

        if (!debut_env)
        {
            debut_env = true;
        }

        if (strncmp(var, tampon_ligne, n_str_env_compteur) == 0)
        {
            for(unsigned int i2=0; i2<taille_tampon_id; ++i2)
            {
                const char c = tampon_ligne[n_str_env_compteur+1+i2]; /* +2 pour ':' et un espace */
                if (!c || (c=='\n') || (c=='\r'))
                {
                    tampon_val[i2] = '\0';
                    break;
                }
                tampon_val[i2] = c;
            }
            tampon_val[taille_tampon_id-1] = '\0';
            ret += tampon_val;
            break;
        }
    }

    fichier.close();
//    if (!ret.isEmpty()) qDebug() << "Bordel, on a la variable" << var << ":" << ret;

    return ret;
}

QString &ScriptAutomate::FormateMessageUtilisateur(QString &s)
{
    return s.replace("\n", "\\n");
}

const char *ScriptAutomate::EnvironnementIdAutomate(int var) const
/* Renvoi la chaine de caractère représentant la commande de variable d'environnement (d'après son identifiant). */
{
//    for(unsigned int i=0; i<LangageAutomate::N_Environnement; ++i)
//    {
//        if (var == LangageAutomate::Environnement_Str[i].var)
//        {
//            return LangageAutomate::Environnement_Str[i].mot;
//        }
//    }
//    return 0;
    return EnvironnementId(var);
}

const char *ScriptAutomate::CommandeIdAutomate(int contexte, int cmd) const
/* Renvoi la chaine de caractère représentant la commande (d'après son identifiant) donnée en argument */
{
    const LangageAutomate::sym *t = ((contexte == LangageAutomate::CONTEXTE_CMD) ? LangageAutomate::Commandes_Str :
                                    ((contexte == LangageAutomate::CONTEXTE_TRACE) ? LangageAutomate::CommandesTrace_Str :
                                    ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::Arguments_Str :
                                        LangageAutomate::ParametresP3D_Str)));

    const unsigned int n_t = ((contexte == LangageAutomate::CONTEXTE_CMD) ? LangageAutomate::N_CommandesLangage :
                             ((contexte == LangageAutomate::CONTEXTE_TRACE) ? LangageAutomate::N_CommandesTrace :
                             ((contexte == LangageAutomate::CONTEXTE_ARGUMENT) ? LangageAutomate::N_ArgumentsLangage :
                                                                         LangageAutomate::N_ParametresP3D)));

    static const char *lang_fr = ScriptAutomate::EnvironnementIdAutomate(LangageAutomate::FRANCAIS);
    static const char *lang_en = ScriptAutomate::EnvironnementIdAutomate(LangageAutomate::ANGLAIS);

    if (!lang_fr || !lang_en)
    {
        return nullptr;
    }

    for(unsigned int i=0; i<n_t; ++i)
    {
        if (cmd == t[i].cmd)
        {
            if (cmp_automate(Langue, lang_fr))
            {
                return t[i].mot_fr;
            }
            else if (cmp_automate(Langue, lang_en))
            {
                return t[i].mot_en;
            }
            else
            {
                break;
            }
        }
    }
    return nullptr;
}

bool ScriptAutomate::ecriture(const QString &str, bool force, bool gere_indent)
{
#ifdef DEBUG
//    qDebug() << "ScriptAutomate::ecriture(..., force=" << force << ", gere_indent=" << gere_indent << ") :: " << str;
#endif // DEBUG

    if (ValideF || force)
    {
        if (gere_indent && profondeur_blocs > 0)
        {
            const unsigned int n = (profondeur_blocs < 31) ? profondeur_blocs * 4 : 128;
            for(unsigned int i=0; i<n; ++i)
            {
                indentation[i] = ' ';
            }
            indentation[n] = '\0';
            qint64 ri = Fichier.write(indentation);
            if (ri != -1)
            {
                qint64 r = Fichier.write(str.toUtf8());
                if (r != -1)
                {
                    return true;
                }
                else
                {
                    ValideF = false;
                    return false;
                }
            }
            else
            {
                ValideF = false;
                return false;
            }
        }
        else
        {
            qint64 r = Fichier.write(str.toUtf8());
            if (r != -1)
            {
                return true;
            }
            else
            {
                ValideF = false;
                return false;
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutInclusionFichier(const QString &nom_fichier)
{
    const char *inc = EnvironnementIdAutomate(LangageAutomate::ENV_INCLURE_FICHIER);
    if (inc)
        return ecriture(QString(inc) + " " + nom_fichier + STR_SAUT_LIGNE, true);
    return false;
}

bool ScriptAutomate::AjoutEnvironnement(bool mode_ajout)
{
    const char *lang = EnvironnementIdAutomate(LangageAutomate::ENV_LANGUE);
//    const char *val_lang = EnvironnementIdAutomate(Langue);

    bool valide_env = true;

    if (!lang) //!val_lang
    {
        return false; /* Le paramètre de langue est obligatoire ! */
    }

//    if (!ecriture(QString(lang) + " " + val_lang + STR_SAUT_LIGNE, true))
    if (!ecriture(QString(lang) + " " + Langue + STR_SAUT_LIGNE, true))
    {
        return false;
    }

    const char *version = EnvironnementIdAutomate(LangageAutomate::ENV_VERSION);
    if (version)
    {
        if (!ecriture(QString(version) + " " + Locale.toString(LangageAutomate::Version) + STR_SAUT_LIGNE, true))
        {
            valide_env = false;
        }
    }

    const char *anim = EnvironnementIdAutomate(LangageAutomate::ENV_ANIMATION);
    if (anim)
    {
        if (!ecriture(QString(anim) + " " + Locale.toString(Animation) + STR_SAUT_LIGNE, true))
        {
            valide_env = false;
        }
    }

    if (mode_ajout)
    {
        const char *compteur = EnvironnementIdAutomate(LangageAutomate::ENV_COMPTEUR_FICHIER);
        if (compteur)
        {
            if (!ecriture(QString(compteur) + " " + Locale.toString(id_compteur_fichier) + STR_SAUT_LIGNE, true))
            {
                valide_env = false;
            }
        }
    }

    if (valide_env)
    {
        return ecriture(QString("\n"), true);
    }
    return false;
}

bool ScriptAutomate::AjoutQuitte()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_QUITTE);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutArret()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ARRET);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutOuvrirFichier(const QString &nom_fichier)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_OUVRIR);
        if (s)
        {
            return ecriture(QString(s) + " \"" + nom_fichier + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutFermerPlan()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_FERMER2D);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutCommentaire(const QString &commentaire)
{
    if (ValideF)
    {
        return ecriture(QString(LangageAutomate::StrCommentaire) + " " + commentaire + STR_SAUT_LIGNE);
    }
    return false;
}

bool ScriptAutomate::AjoutReinitGlobal()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_REINIT);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}


bool ScriptAutomate::AjoutReinit2D()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_REINIT_VUES);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutVueExtrusion(const QPointF &p1, const QPointF &p2, bool p2_absolu)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_VUE);
        if (s_cmd)
        {
            const char *s_arg = CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEEXTRUSION);
            const char *s_arg2 = p2_absolu ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_ABSOLU)) : (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_RELATIF));

            if (s_arg && s_arg2)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + s_arg2 + " " + Locale.toString(p1.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p1.y(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutVueRevolution(const QPointF &p1, const QPointF &p2, bool p2_absolu)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_VUE);
        if (s_cmd)
        {
            const char *s_arg = CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEREVOLUTION);
            const char *s_arg2 = p2_absolu ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_ABSOLU)) : (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_RELATIF));

            if (s_arg && s_arg2)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + s_arg2 + " " + Locale.toString(p1.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p1.y(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutVue2D(const QPointF &p1, const QPointF &p2, Perspective3D::vues2D_t vue, bool p2_absolu)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_VUE);
        if (s_cmd)
        {
            const char *s_arg = ((vue == Perspective3D::vues2D_t::VUEFACE) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEFACE)) :
                                 (vue == Perspective3D::vues2D_t::VUECOTE) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUECOTE)) :
                                 (vue == Perspective3D::vues2D_t::VUEHAUT) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEHAUT)) :
                                 (vue == Perspective3D::vues2D_t::VUEMULT) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEMULT)) :
                                                                  (nullptr) );

            const char *s_arg2 = p2_absolu ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_ABSOLU)) : (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_RELATIF));

            if (s_arg && s_arg2)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + s_arg2 + " " + Locale.toString(p1.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p1.y(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(p2.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutOrigine(const QPointF &origine, Perspective3D::vues2D_t vue)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ORIGINE);
        if (s_cmd)
        {
            const char *s_arg = ((vue == Perspective3D::vues2D_t::VUEFACE) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEFACE)) :
                                 (vue == Perspective3D::vues2D_t::VUECOTE) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUECOTE)) :
                                 (vue == Perspective3D::vues2D_t::VUEHAUT) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEHAUT)) :
                                 (vue == Perspective3D::vues2D_t::VUEMULT) ? (CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEMULT)) :
                                                                  (nullptr) );
            if (s_arg)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + Locale.toString(origine.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(origine.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutOrigineExtrusion(const QPointF &origine)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ORIGINE);
        if (s_cmd)
        {
            const char *s_arg = CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEEXTRUSION);
            if (s_arg)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + Locale.toString(origine.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(origine.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutOrigineRevolution(const QPointF &origine)
{
    if (ValideF)
    {
        const char *s_cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ORIGINE);
        if (s_cmd)
        {
            const char *s_arg = CommandeIdAutomate(LangageAutomate::CONTEXTE_ARGUMENT, LangageAutomate::ARG_VUEREVOLUTION);
            if (s_arg)
            {
                return ecriture(QString(s_cmd) + " " + s_arg + " " + Locale.toString(origine.x(), 'f', PRECISION_DECIMAUX) + " " + Locale.toString(origine.y(), 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutGen3D(qreal param1, bool non_bloquant_generation)
{
    if (ValideF)
    {
        const char *s = (non_bloquant_generation ? CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_GEN3D_NON_BLOQUANT):
                                                  CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_GEN3D));
        if (s)
        {
            if (!CompareE(param1, 0))
            {
                return ecriture(QString(s) + " " + Locale.toString(param1, 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutExport3D(const QString &nom_fichier)
/* Attention, l'enregistrement de cette commande est hasardeux, dans le sens où elle est refusée par défaut dans le configuration */
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_EXPORT3D);
        if (s)
        {
            QString nfichier(nom_fichier);
            SupprimeCharsSpeciauxStr(nfichier);
            return ecriture(QString(s) + " " + nom_fichier + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutGen3DBloquant()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_GEN3D_BLOQUANT);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutBloc(const QString &nom_bloc)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_DEF_BLOC);
        if (s)
        {
            bool r = ecriture(QString(STR_SAUT_LIGNE) + QString(s) + " \"" + nom_bloc + "\"" STR_SAUT_LIGNE + LangageAutomate::OuvertureBlocInstruction + STR_SAUT_LIGNE);
            if (r)
            {
                ++profondeur_blocs;
                return true;
            }
            return false;
        }
    }
    return false;
}

bool ScriptAutomate::AjoutClotureBloc()
{
    if (ValideF && (profondeur_blocs > 0))
    {
        --profondeur_blocs;
        return ecriture(QString(LangageAutomate::FermetureBlocInstruction) + STR_SAUT_LIGNE STR_SAUT_LIGNE, false, false);
    }
    return false;
}

bool ScriptAutomate::AjoutExecBloc(const QString &nom_bloc)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_EXEC_BLOC);
        if (s)
        {
            return ecriture(QString(s) + " \"" + nom_bloc + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutMessage(const QString &message)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_MESSAGE);
        if (s)
        {
            QString copie = message;
            return ecriture(QString(s) + " \"" + FormateMessageUtilisateur(copie) + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutDiction(const QString &message)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_DICTE);
        if (s)
        {
            QString copie = message;
            return ecriture(QString(s) + " \"" + FormateMessageUtilisateur(copie) + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutNote(const QString &note)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_NOTE);
        if (s)
        {
            QString copie = note;
            return ecriture(QString(s) + " \"" + FormateMessageUtilisateur(copie) + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}
bool ScriptAutomate::AjoutConfirmationUtilisateur(const QString &message)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_CONFIRMATION);
        if (s)
        {
            QString copie = message;
            return ecriture(QString(s) + " \"" + FormateMessageUtilisateur(copie) + "\"" STR_SAUT_LIGNE);
        }
    }
    return false;
}


bool ScriptAutomate::AjoutDelai(unsigned int temps_ms)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_DELAI);
        if (s)
        {
            return ecriture(QString(s) + " " + Locale.toString(temps_ms) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutAffichageOnglet2D()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ZONE2D);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutAffichageOnglet3D(unsigned int id)
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ONGLET3D);
        if (s)
        {
            return ecriture(QString(s) + " " + Locale.toString(id) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutAnimation3D()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_ANIME_3D);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutChargeConfig()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_CHARGE_CONFIG);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutConfig(const Perspective3D::ParametresPerspective &parametres_p3d)
{
    if (!AjoutChargeConfig())
        return false;
    if (!AjoutParametreTolerance(parametres_p3d.Tolerance))
        return false;
    if (!AjoutParametreEchelleUniforme(parametres_p3d.TailleDiagoUniforme))
        return false;
    if (!AjoutParametreArrondi(parametres_p3d.Arrondi_dessin))
        return false;
    if (!AjoutParametreDivisionsCourbes(parametres_p3d.Divisionscercles))
        return false;
    if (!AjoutParametreProjections(parametres_p3d.Projections))
        return false;
    if (!AjoutParametreInformations3D(parametres_p3d.Informations))
        return false;
    if (!AjoutParametreTailleTexte3D(parametres_p3d.Tailletexte3d))
        return false;
    if (!AjoutParametreModeFilaire(parametres_p3d.Filaire_uniquement))
        return false;
    if (!AjoutParametreParametres3D(parametres_p3d.Parametres3d))
        return false;
    if (!AjoutParametreTailleMaxSurfaces(parametres_p3d.Taille_surfaces))
        return false;
    if (!AjoutParametreNormeVues(parametres_p3d.Norme_vues))
        return false;
    if (!AjoutParametreIdRAL(parametres_p3d.Id_ral))
        return false;
//    if (!AjoutParametreMultiThreading(parametres_p3d.MultiThreading)) /* Laisser le paramètre utilisateur inchangé... */
//        return false;
    return true;
}

bool ScriptAutomate::AjoutForceConfig()
{
    if (ValideF)
    {
        const char *s = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_FORCE_CONFIG);
        if (s)
        {
            return ecriture(QString(s) + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreTolerance(double val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_TOLERANCE);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val, 'f', PRECISION_DECIMAUX) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreEchelleUniforme(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_DIAGO_ECHELLE_UNIFORME);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreArrondi(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_ARRONDI);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreDivisionsCourbes(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_DIVIS_COURBES);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreProjections(Perspective3D::vues2D_t val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_PROJS);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(PENUM_CAST_INT(val)) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreInformations3D(Perspective3D::infos3d_t val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_INFOS);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(PENUM_CAST_INT(val)) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreTailleTexte3D(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_TAILLE_TEXTE_3D);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreModeFilaire(bool val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_FILAIRE);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreParametres3D(Perspective3D::params_gen3d_t val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_PARAMS3D);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(PENUM_CAST_INT(val)) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreTailleMaxSurfaces(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_TAILLE_MAX_SURFACES);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreNormeVues(Perspective3D::normevues_t val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_NORME_VUES);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(PENUM_CAST_INT(val)) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreIdRAL(unsigned int val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_ID_RAL);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutParametreMultiThreading(bool val)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_PARAM);
        if (cmd)
        {
            const char *type_param = CommandeIdAutomate(LangageAutomate::CONTEXTE_PARAM, LangageAutomate::PARAM_MULTI_THREADING);

            if (type_param)
            {
                return ecriture(QString(cmd) + " " + type_param + " " + Locale.toString(val) + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}

bool ScriptAutomate::AjoutScene2D(const Perspective3D::PScene2D &scene)
{
    if (ValideF)
    {
        const Perspective3D::PPixel couleur_trace(250, 250, 250);

        const char *cmd_groupe = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_GROUPE);
        const char *cmd_couleur = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_COULEUR);
        const char *cmd_ligne = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_LIGNE);
        const char *cmd_cercle = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_CERCLE);
        const char *cmd_ellipse = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_ELLIPSE);
        const char *cmd_courbe = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_COURBE);
        const char *cmd_texte = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_TEXTE);
        const char *cmd_point = CommandeIdAutomate(LangageAutomate::CONTEXTE_TRACE, LangageAutomate::TRACE_POINT);

        short cpt_invalide = 0;

        if (cmd_groupe && cmd_couleur && cmd_ligne && cmd_cercle && cmd_ellipse && cmd_courbe && cmd_texte && cmd_point)
        {
            const Perspective3D::PStdVectGroupeEnts2D &groupes_perspective = scene.Groupes();
            const pint n_groupes_perspective = groupes_perspective.size();

            for(pint ig=0; ig<n_groupes_perspective; ++ig)
            {
                if (!groupes_perspective[ig])
                {
                    break;
                }
                const Perspective3D::PGroupeEnts2D &groupe_p = *(groupes_perspective[ig]);

                if (!groupe_p.Valide())
                {
                    continue;
                }

                if (!ecriture(QString(cmd_groupe) + STR_SAUT_LIGNE))
                {
                    ++cpt_invalide;
                }

                if (!ecriture(QString(cmd_couleur) + " " + Locale.toString(static_cast<float>(couleur_trace.R())/255.) + " " +
                              Locale.toString(static_cast<float>(couleur_trace.V())/255.) + " " +
                              Locale.toString(static_cast<float>(couleur_trace.B())/255.) + STR_SAUT_LIGNE))
                {
                    ++cpt_invalide;
                }

                const Perspective3D::PStdVectLignes2D &vectsegments = groupe_p.ListeLignes();
                for (unsigned int i=0; i<vectsegments.size(); i++)
                {
                    const Perspective3D::Pligne2D &s = vectsegments[i];

                    if (!ecriture(QString(cmd_ligne) + " " + Locale.toString(s.P1Const().X()) + " " + Locale.toString(s.P1Const().Y()) + " " +
                                  Locale.toString(s.P2Const().X()) + " " + Locale.toString(s.P2Const().Y()) + STR_SAUT_LIGNE))
                    {
                        ++cpt_invalide;
                    }
                }

                const Perspective3D::PStdVectEllipses2D &courbes = groupe_p.ListeEllipses();
                for(unsigned int i=0; i<courbes.size(); i++)
                {
                    const Perspective3D::Pellipse2D &c = courbes[i];

                    if (c.Ferme()) /* Cercle fermé ou ellipse */
                    {
                        if (CompareE(c.Ht(), c.Lg())) /* Cercle */
                        {
                            if (!ecriture(QString(cmd_cercle) + " " + Locale.toString(c.CentreConst().X()) + " " + Locale.toString(c.CentreConst().Y()) + " " + Locale.toString(c.Ht()) + STR_SAUT_LIGNE))
                            {
                                ++cpt_invalide;
                            }
                        }
                        else /* Ellipse */
                        {
                            if (!ecriture(QString(cmd_ellipse) + " " + Locale.toString(c.CentreConst().X()) + " " + Locale.toString(c.CentreConst().Y()) + " " + Locale.toString(c.Lg()) + " " + Locale.toString(c.Ht()) + " " + Locale.toString(c.Rotation()) + STR_SAUT_LIGNE))
                            {
                                ++cpt_invalide;
                            }
                        }
                    }
                    else /* Courbe */
                    {
                        if (!ecriture(QString(cmd_courbe) + " " + Locale.toString(c.CentreConst().X()) + " " + Locale.toString(c.CentreConst().Y()) + " " + Locale.toString(c.Lg()) + " " + Locale.toString(c.Ht()) + " " +
                                       Locale.toString(c.Angle1()) + " " + Locale.toString(c.Angle2()) + " " + Locale.toString(c.Rotation()) + STR_SAUT_LIGNE))
                        {
                            ++cpt_invalide;
                        }
                    }
                }

                const Perspective3D::PStdVectTextes2D &textes = groupe_p.ListeTextes();
                for(unsigned int i=0; i<textes.size(); i++)
                {
                    const Perspective3D::PTexte2D &t = textes[i];
                    const QString str_txt(t.Texte());
                    if (!ecriture(QString(cmd_texte) + " \"" + str_txt + "\" " + Locale.toString(t.Position().X()) + " " + Locale.toString(t.Position().Y()) + " " +
                                   Locale.toString(t.TailleTexte()) + " " + Locale.toString(t.Rotation()) + STR_SAUT_LIGNE))
                    {
                        ++cpt_invalide;
                    }
                }

                const Perspective3D::PStdVectPoints2D &points = groupe_p.ListePoints();
                for(unsigned int i=0; i<points.size(); i++)
                {
                    const Perspective3D::Ppoint2D &p = points[i];
                    if (!ecriture(QString(cmd_point) + " " + Locale.toString(p.X()) + " " + Locale.toString(p.Y()) + STR_SAUT_LIGNE))
                    {
                        ++cpt_invalide;
                    }
                }
            }

            return cpt_invalide == 0;
        }
    }
    return false;
}

bool ScriptAutomate::AjoutEval(const QString &code_eval)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_EXEC_CALC);
        if (cmd)
        {
            return ecriture(QString(cmd) + " \"" + code_eval + "\"" + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutBoucleEval(const QString &code_eval, const QString &nom_bloc_boucle)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_BOUCLE_CALC);
        if (cmd)
        {
            return ecriture(QString(cmd) + " \"" + code_eval + "\" \"" + nom_bloc_boucle + "\"" + STR_SAUT_LIGNE);
        }
    }
    return false;
}

bool ScriptAutomate::AjoutCondEval(const QString &code_eval, const QString &nom_bloc_si, const QString &nom_bloc_sinon)
{
    if (ValideF)
    {
        const char *cmd = CommandeIdAutomate(LangageAutomate::CONTEXTE_CMD, LangageAutomate::CMD_COND_CALC);
        if (cmd)
        {
            if (nom_bloc_sinon.isEmpty())
            {
                return ecriture(QString(cmd) + " \"" + code_eval + "\" \"" + nom_bloc_si + "\"" + STR_SAUT_LIGNE);
            }
            else
            {
                return ecriture(QString(cmd) + " \"" + code_eval + "\" \"" + nom_bloc_si + "\" \"" + nom_bloc_sinon + "\"" + STR_SAUT_LIGNE);
            }
        }
    }
    return false;
}
