/**
© 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  informatiques 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.
**/

/* Gestion de l'automate (lecture et écriture, chacun dans une classe différente). */

#ifndef AUTOMATE_H
#define AUTOMATE_H

#include <QTimer>
#include <QObject>
#include <QString>
#include <QRectF>
#include <QPointF>

#include "zcalc.h"
#include "parametres.h"
#include "API/perspective_api.h"

namespace LangageAutomate
{
    enum LanguesScripts { FRANCAIS=0, ANGLAIS };
}

enum RET_EXEC_AUTOMATE { AUTOMATE_ERR=0, AUTOMATE_ARRET, AUTOMATE_OK };
#define ExtensionScriptsAutomate "z6s"

class MoteurAutomate;

inline int ConvLangueP3DAutomate(Perspective3D::i18n::lang_p3d_t lang_p3d)
{
    return (lang_p3d == Perspective3D::i18n::lang_p3d_t::P3D_LANG_FR) ? LangageAutomate::FRANCAIS : LangageAutomate::ANGLAIS;
}

/* Interpreteur de l'automate */
class Automate : public QObject
{
        Q_OBJECT
    public:
        explicit Automate(const QString &chemin_zetta6);
        ~Automate();

        void defConfig(Parametres *config);

        void defExecMultiThread(bool m) { execMultithread=m; }

        int execFichier(const QString &nfichier, bool desactive_temporisation=false);
        int exec(const QString &code, const QString &chemin_reference=QString(), bool desactive_temporisation=false);

        bool ExecEnCours() const;
        bool Pause();
        bool Continue();
        bool ArretUtilisateur();
        bool Stop(bool err=false);

        bool AccepteNavigation3D() const;
        bool AccepteExport3D() const;

        inline bool AnimationScript() const { return animation_script; }
        inline int IdRevisionScript() const { return id_compteur_fichier; }

        const QString& DerniereErreur() const;

    protected:
        bool ExecEntete();

        int EnvironnementAutomate(const QString &mot) const;
        int CommandeAutomate(const QString &mot, int contexte) const;

        void MessageFmtErreur(const QString &mesg, int ligne=-1);
        void MessageFmt(const QString &mesg, int ligne=-1, bool mesg_utilisateur=false) const;
        void AfficheVariable(const QString &var, const QString &val) const;
        void AjoutJournal(const QString &commande, const QStringList &arguments, bool exec_pas_a_pas=false);
        QString &FormateMessageUtilisateur(QString &s) const;
        QString FormateArgumentsMessage(const QStringList &arguments, bool diction=false) const;

        int LangueDiction() const;

        bool AssigneBool(bool &b, const QString &s) const;
        bool AssigneUInt(unsigned int &i, const QString &s) const;
        bool AssigneInt(int &i, const QString &s) const;
        bool AssigneUshort(unsigned short &i, const QString &s) const;
        bool AssigneShort(short &i, const QString &s) const;
        bool AssigneFloat(float &f, const QString &s) const;
        bool AssigneDouble(double &d, const QString &s) const;
        bool AssignePfloat(pfloat &d, const QString &s) const;
        bool AssigneCoords(const QStringList &arguments, float *coords, unsigned int decalage, unsigned int nb_coords) const;

        bool InclureFichier(int id_ligne_courante, const QString &argument);

        bool execCycle(bool mode_interpreteur, bool uniquement_entete=false);
        bool execLigne(int id_ligne, bool mode_interpreteur, bool uniquement_entete=false);
        bool execBloc(int id_ligne, const QString &nom_bloc, bool boucle=false);
        bool execEnvLigne(int id_ligne, const QString &commande, const QString &argument, bool mode_interpreteur);
        bool execCodeLigne(int id_ligne, const QString &commande, const QStringList &arguments, bool mode_interpreteur);
        bool ChargeConfig(); /* Charge la configuration de Zetta6 */

#ifdef DEBUG
        void AffichePile(const char *entete=0) const;
#endif

    protected:
        /* Variables pour le script en cours d'exécution: */
        QString CheminReference;
        QString CheminZetta6;
        QString mesg_derniere_erreur;
        MoteurAutomate *Moteur;

        int Langue;
        int RedefTempo;
        bool execution_verbeuse;
        bool accepte_nav3d;
        bool accepte_export3d;
        bool en_cours;
        bool pause;
        bool force_arret;
        bool config_chargee;
        bool animation_script;
        bool execMultithread;
        int id_compteur_fichier;
        /* *** */

        Zcalc Calc;
        Perspective3D::ParametresPerspective parametres_p3d;
        QRgb couleur_trace;
        Parametres const *parametres_zetta6;
        QTimer Tempo;

    signals:
        void ExecutionLigne(int, bool);
        void Message(const QString &, bool) const;
        void Dicte(const QString &, int langue) const;
        void Confirmation(const QString &) const;
        void NouvelleLangueEnv(int langue, bool interpreteur);

        void CadrageVue(bool);

        void TraceGroupe();
        void TraceLigne(qreal, qreal, qreal, qreal, QRgb);
        void TraceCourbe(qreal, qreal, qreal, qreal, qreal, qreal, qreal, QRgb);
        void TraceRect(qreal, qreal, qreal, qreal, QRgb);
        void TraceTexte(const QString &, qreal, qreal, qreal, qreal, QRgb);
        void TracePoint(qreal, qreal, QRgb);

        void ReinitInterface(bool) const; /* Réinitialisation de l'environnement de travail */
        void OuvrirFichier(const QString &) const; /* Ouvre un plan */
        void FermeFichier(); /* Ferme le plan */
        void Onglet2D(); /* Passe sur l'onglet 2D. */
        void DefVue(Perspective3D::vues2D_t, QRectF) const; /* Définition d'une vue (type vue/cadre) */
        void DefOrigine(Perspective3D::vues2D_t, QPointF) const; /* Définition d'une origine */
        void Generation(const Perspective3D::ParametresPerspective &parametres, bool bloquant, qreal param1) const; /* Lance la génération */
        void Export3D(const QString &nom_fichier); /* Export 3D après génération. */
        void FermeGeneration() const; /* Ferme le dernier modèle généré */
        void ForceConfig(const Perspective3D::ParametresPerspective &config) const; /* Demande à l'utilisateur de réécrire la configuration liée à la génération 3D */
        void Onglet3D(int id);
        void Anime3D();
        void FinScript();

        void Question_Bool(const QString &, bool, bool *, bool *);
        void Question_Int(const QString &, int, int *, bool *);
        void Question_Float(const QString &, double, double *, bool *);

    private slots:
        void execTempo();
        void Message_calc(const QString &m);
};

class Th_Automate: public QThread
{
        Q_OBJECT
    public:
        explicit Th_Automate(const QString &chemin_zetta6);
        ~Th_Automate();

        void defFichierExec(const QString &fichier);

        void run();

        inline const Automate &AutomateConst() const { return automate; }
        inline Automate &AutomateNonConst() { return automate; }
        inline const Automate *AutomatePtr() const { return &automate; }

        inline int ResultatExec() const { return resultat_exec; }

        int execFichier(bool multi_thread=false);

    protected:
        Automate automate;
        QString fichier_exec;
        int resultat_exec;
};

/* Interpréteur d'exécution en ligne de commande */
class Interpreteur : public Automate
{
        Q_OBJECT

    public:
        explicit Interpreteur(const QString &chemin_reference, int langue);
        ~Interpreteur();

        bool defLangue(int langue);
        bool defConfig(Parametres *config);

        bool ListeCommandes(QStringList &liste) const;
        int execCommande(const QString &cmd);
};

/* Ecriture des scripts pour l'automate. */
class ScriptAutomate
{
    public:
        ScriptAutomate(const QString &nom_fichier, int langue=LangageAutomate::FRANCAIS, bool animation=false, bool mode_ajout=false);
        ~ScriptAutomate();

        inline int IdRevisionScript() const { return id_compteur_fichier; }

        bool AjoutQuitte();
        bool AjoutArret();
        bool AjoutInclusionFichier(const QString &nom_fichier);

        bool AjoutOuvrirFichier(const QString &nom_fichier);
        bool AjoutFermerPlan();

        bool AjoutCommentaire(const QString &commentaire);
        bool AjoutReinitGlobal();
        bool AjoutReinit2D();

        bool AjoutVue2D(const QPointF &p1, const QPointF &p2, Perspective3D::vues2D_t vue, bool p2_absolu=false);
        bool AjoutVueExtrusion(const QPointF &p1, const QPointF &p2, bool p2_absolu);
        bool AjoutVueRevolution(const QPointF &p1, const QPointF &p2, bool p2_absolu);

        bool AjoutOrigine(const QPointF &origine, Perspective3D::vues2D_t vue);
        bool AjoutOrigineExtrusion(const QPointF &origine);
        bool AjoutOrigineRevolution(const QPointF &origine);

        bool AjoutGen3D(qreal param1=0, bool non_bloquant_generation=false);
        bool AjoutGen3DBloquant();

        bool AjoutExport3D(const QString &nom_fichier);

        bool AjoutBloc(const QString &nom_bloc);
        bool AjoutClotureBloc();
        bool AjoutExecBloc(const QString &nom_bloc);

        bool AjoutMessage(const QString &message);
        bool AjoutDiction(const QString &message);
        bool AjoutNote(const QString &note);
        bool AjoutConfirmationUtilisateur(const QString &message);

        bool AjoutDelai(unsigned int temps_ms);
        bool AjoutAffichageOnglet2D();
        bool AjoutAffichageOnglet3D(unsigned int id);
        bool AjoutAnimation3D();

        bool AjoutChargeConfig();
        bool AjoutConfig(const Perspective3D::ParametresPerspective &parametres_p3d);
        bool AjoutForceConfig();

        bool AjoutParametreTolerance(double val);
        bool AjoutParametreEchelleUniforme(unsigned int val);
        bool AjoutParametreArrondi(unsigned int val);
        bool AjoutParametreDivisionsCourbes(unsigned int val);
        bool AjoutParametreProjections(Perspective3D::vues2D_t val);
        bool AjoutParametreInformations3D(Perspective3D::infos3d_t val);
        bool AjoutParametreTailleTexte3D(unsigned int val);
        bool AjoutParametreModeFilaire(bool val);
        bool AjoutParametreParametres3D(Perspective3D::params_gen3d_t val);
        bool AjoutParametreTailleMaxSurfaces(unsigned int val);
        bool AjoutParametreNormeVues(Perspective3D::normevues_t val);
        bool AjoutParametreIdRAL(unsigned int val);
        bool AjoutParametreMultiThreading(bool val);

        bool AjoutScene2D(const Perspective3D::PScene2D &scene);

        /* Partie gestion Evaluateur: */
        bool AjoutEval(const QString &code_eval);
        bool AjoutBoucleEval(const QString &code_eval, const QString &nom_bloc_boucle);
        bool AjoutCondEval(const QString &code_eval, const QString &nom_bloc_si, const QString &nom_bloc_sinon=QString());

        inline bool Valide() const { return ValideF ; }

    protected:
        QString &FormateMessageUtilisateur(QString &s);
        bool AjoutEnvironnement(bool mode_ajout);
        const char *EnvironnementIdAutomate(int var) const;
        const char *CommandeIdAutomate(int contexte, int cmd) const;
        bool ecriture(const QString &str, bool force=false, bool gere_indent=true);
        QString VarEnvironnementScript(const QString &chemin_script, const char *var) const;

    private:
        char indentation[129];
        bool Animation;
        bool ValideF;
        QFile Fichier;
        QString Langue;
        int id_compteur_fichier;
        int profondeur_blocs;
};

#endif // AUTOMATE_H
