// This file is part of the Herezh++ application.
//
// The finite element software Herezh++ is dedicated to the field
// of mechanics for large transformations of solid structures.
// It is developed by Gérard Rio (APP: IDDN.FR.010.0106078.000.R.P.2006.035.20600)
// INSTITUT DE RECHERCHE DUPUY DE LÔME (IRDL) <https://www.irdl.fr/>.
//
// Herezh++ is distributed under GPL 3 license ou ultérieure.
//
// Copyright (C) 1997-2022 Université Bretagne Sud (France)
// AUTHOR : Gérard Rio
// E-MAIL  : gerardrio56@free.fr
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// For more information, please consult: <https://herezh.irdl.fr/>.

#include "Animation_vrml.h"

#include <iomanip>
#include "CharUtil.h"

    // CONSTRUCTEURS :
// par defaut
Animation_vrml::Animation_vrml () :
   OrdreVisu(".........................................animation"
             ,"definition de l'animation si necessaire","ani")
   ,cycleInterval(8),loop(false),startTime(1),stopTime(0)
   ,debut_auto(false),inter_2pas(2.)
   ,isovaleurs_vrml(NULL),choix_mail(NULL)
   ,choix_deformee(NULL)          
     {}; 
     
     // constructeur de copie 
Animation_vrml::Animation_vrml (const Animation_vrml& ord) :
    OrdreVisu(ord)
    ,cycleInterval(ord.cycleInterval),loop(ord.loop)
    ,startTime(ord.startTime),stopTime(ord.stopTime)
    ,debut_auto(ord.debut_auto),inter_2pas(ord.inter_2pas)
    ,isovaleurs_vrml(ord.isovaleurs_vrml)
    ,choix_mail(ord.choix_mail),choix_deformee(ord.choix_deformee)
      { };
               
    // DESTRUCTEUR :
Animation_vrml::~Animation_vrml () 
     {};  
    
    // METHODES PUBLIQUES :
// execution de l'ordre
// tab_mail : donne les numéros de maillage concerné
// incre : numéro d'incrément qui en cours
// type_incre : indique si c'est le premier le dernier ou l'incrément courant a visualiser ou pas
// animation : indique si l'on est en animation ou pas
// unseul_incre : indique si oui ou non il y a un seul increment à visualiser
void Animation_vrml::ExeOrdre(ParaGlob * paraGlob,const Tableau <int>& ,LesMaillages * ,bool ,LesReferences*
                      ,LesLoisDeComp* ,DiversStockage*,Charge*,LesCondLim*
                      ,LesContacts*,Resultats*,UtilLecture & entreePrinc,OrdreVisu::EnumTypeIncre ,int 
                      ,bool animation,const map < string, const double * , std::less <string> >&
                      ,const List_io < TypeQuelconque >& listeVecGlob)
{ostream &sort = entreePrinc.Sort_princ_vrml();
 // définition de l'animation uniquement si animation est vraie
 if (actif && animation)
 {if (list_nom_coordinate.size() == 0)
   // cas ou on n'a pas définit les coordonnées c'est-a-dire que l'ordre déformée
   // n'a pas été exécutée, on ne peut rien faire sinon prévenir de définir la déformée
   { cout << "\n les parametres de deformee ne sont pas definit, l'animation ne "
           << " peut-être exécutee !! "
           << "\n recommencee en définissant la deformee ";
   }
   else        
   { // on défini le trigger qui permet le démarrage de l'animation
     // dans le cas d'un démarrage non automatique
     if (!debut_auto) // cas d'un début non automatique
        sort << "\n #  Trigger things on touch: cas d'un demarrage non automatique"
             << "\n DEF Touch TouchSensor { } ";
     // on défini le timer
     sort << "\n DEF Clock TimeSensor {"
          << "\n 	                  cycleInterval " << cycleInterval
          << "\n 	                  loop          " ;
          if (loop) sort << "TRUE"; else sort << "FALSE";
     sort << "\n 	                  startTime     " << startTime
          << "\n 	                  stopTime      " <<  stopTime
          << "\n                       } \n";
          
     // ------ on défini l' interpolateur de points ----
     // récup du nombre de maillage qui sont visualisé
     const list <int> & lis_mail_choisit = choix_mail->List_choisit();
     int nb_maillage= lis_mail_choisit.size();
     // on fractionne régulièrement le temps
     int nb_step = (int) (list_nom_coordinate.size()/nb_maillage);
     // nb_step donne le nombre d'incrément
     double step_time = 1./(nb_step-1);
     // boucle sur les maillages
     for (int imail=1;imail<=nb_maillage;imail++)
      {// def de l'interpolateur de coordonnées
       sort << "\n DEF InterpolDePoints" << imail << " CoordinateInterpolator { "
            << "\n   key [ ";
       for (int i=1; i<= nb_step-1; i++)
         sort << " " << step_time*(i-1);
       sort << " " << 1 << "] ";
       // on écrit les coordonnées
       sort << "\n     keyValue [ \n";
       list <list <Coordonnee > >::iterator il,ilfin = list_coordinate.end();
       list <Coordonnee >::iterator ik,ikfin;
       int compte;
       // on décale le début de il en fonction du maillage traité
       il=list_coordinate.begin();for (int ib=2;ib<=imail;ib++)
        {il++;if(il==ilfin) {cout << "\n erreur de sortie d'animation vrml";Sortie(1);}};
       for (compte=1;il!=ilfin;) // l'incrémentation de il est multiple donc traitée dans la boucle
          { ikfin = il->end();
            for (ik=il->begin();ik!=ikfin;ik++,compte++)
              { sort << " " << setw (16);  (*ik).Affiche(sort,16);
                // si l'on n'est pas en dimension 3 on complete avec des zeros
                if (paraGlob->Dimension () == 2)
                    { sort << setw (16) << 0 <<" ";}
                else if (paraGlob->Dimension () == 1)
                     { sort << setw (16) << 0  <<" " << setw (16) << 0  <<" ";}
                sort <<  " , ";
                if (compte == 2)
                 { compte = 1; sort << "\n";}
               }
            // on s'occupe d'incrémenter il
            for (int ib=1;ib<=nb_maillage;ib++)
              {il++; if (il== ilfin) break;};
           }
         sort << "\n     ] } ";
       }
     // ------ on défini l' interpolateur de couleur éventuellement ----
     // pour l'instant ne sert pas car  il faut un script !!
 /*    if (isovaleurs_vrml->Actif())
      {  sort << "\n DEF InterpolDeCouleurs ColorInterpolator { "
          << "\n   key [ ";
     // on fractionne régulièrement le temps
     int nb_step = (int) list_nom_coordinate.size();
     double step_time = 1./(nb_step-1);
     for (int i=1; i<= nb_step-1; i++)
       sort << " " << step_time*(i-1);
     sort << " " << 1 << "] ";
     // def d'un conteneur intermédiaire pour simplifier
     const list < Tableau < Rgb > > & list_couleur = isovaleurs_vrml->List_couleur();

     // on écrit les couleurs
     sort << "\n     keyValue [ \n";
     list <Tableau <Rgb > >::const_iterator il,ilfin = list_couleur.end();
     int compte;
     for (il=list_couleur.begin(),compte=1;il!=ilfin;il++)
       { int taill=(*il).Taille();
         for (int ik=1;ik<=taill;ik++,compte++)
           { sort << " " << setw (16) <<  (*il)(ik);
             sort <<  " , ";
             if (compte == 2)
              { compte = 1; sort << "\n";
               }
            }
        }
     sort << "\n     ] } ";
       } */
    // on définit les interpolateurs scalaires qui permettent de gérer la transparence de chaque
    // incrément. L'idée est que la transparence d'un incrément est active uniquement sur un
    // pas de temps, tout le reste du temps l'incrément est tout transparent
    if (isovaleurs_vrml->Actif())
     { sort << "\n \n # ------ cas des isovaleurs, definition des interpolateurs scalaires pour la transparence "
            << " des matieres ----- ";
       // on a autant d'interpolateur que de step
       double dex=step_time/10;
       for (int ist=1;ist<= nb_step;ist++)
        {sort << "\n DEF inter_intense"<<ist<<" ScalarInterpolator {"
              << "\n   key [ ";
         // on décompose en temps de base
         if (ist==1)
          {// cas du premier interpolateur
           sort << "0.0 "<<(step_time-dex)<<" "; // monté
           for (int i=2; i<= nb_step-1; i++)
             sort << " " << step_time*(i-1);
           }
         else if (ist==nb_step)
          {// cas du dernier interpolateur
           for (int i=1; i<= nb_step-1; i++)
             sort << " " << step_time*(i-1);  // descente
           sort << " "<<(1.-dex)<<" ";
           }
         else // cas courant
          {for (int i=1; i<= nb_step-1; i++)
            { if(i==ist) // cas de la monté
                sort <<" " << step_time*(ist-1) - dex // monté
                     <<" " << step_time*(ist-1)
                     <<" " << step_time*ist - dex; // descente
              // cas courant
              else sort << " " << step_time*(i-1);
             }
           }
         // fin
         sort << " " << 1 << "] ";
         // on écrit les valeurs de transparence
         sort << "\n     keyValue [ ";
         // on décompose en temps de base
         if (ist==1)
          {// cas du premier interpolateur
           sort << "0.0, 0.0,  "; // validité
           for (int i=2; i<= nb_step-1; i++)
             sort << " 1.0, ";
           sort << " 1.0, ";
           }
         else if (ist==nb_step)
          {// cas du dernier interpolateur
           for (int i=1; i<= nb_step-1; i++)
             sort << "1.0, " ;
           sort << " 1.0, 0.0, "; // descente
           }
         else // cas courant
          {for (int i=1; i<= nb_step-1; i++)
            { if(i==ist) // cas de la monté
                sort <<" 1.0, "  // monté
                     <<" 0.0, "
                     <<" 0.0, " ; // descente
              // cas courant
              else sort << " 1.0, " ;
             }
             sort << " 1.0, ";
           }
         sort << "] ";
         sort << "\n    } ";
         }
       }
     
     // on définit maintenant le routage pour le déplacement
     sort << "\n \n # ------ definition de l'horloge ----- ";
     if (!debut_auto) // cas d'un début non automatique
         sort << "\n ROUTE Touch.touchTime TO Clock.set_startTime";
     else // cas d'un début automatique
         { // on défini une deuxième horloge qui fléchée vers la première servira de démarrage auto
           sort << "\n# cas ou on veut un demarrage automatique, on cree une deuxieme horloge"
                << "\n# dont le cycle est flechee vers la premiere,";
           sort << "\n DEF Clock2 TimeSensor {"
                << "\n 	                  cycleInterval " << (cycleInterval+inter_2pas)
                << "\n 	                  loop   TRUE         " ;
           sort << "\n 	                  startTime     " << startTime
                << "\n 	                  stopTime      " <<  -1.
                << "\n                       }";
           sort << "\n ROUTE Clock2.cycleTime TO Clock.set_startTime";
          };
     // def du routage pour la géométrie
     sort << "\n \n # ------ definition du routage pour la deformee de base ----- ";
     // boucle sur les maillages
     for (int imail=1;imail<=nb_maillage;imail++)
      {sort << "\n ROUTE Clock.fraction_changed TO InterpolDePoints"<<imail<<".set_fraction";
       sort << "\n ROUTE InterpolDePoints"<<imail<<".value_changed TO " ;
       // récup du nom des coordonnées initiales pour chaque maillage
       list <string>::iterator ilc = list_nom_coordinate.begin();
       list <string>::iterator ilcfin = list_nom_coordinate.end();
       for (int ib=2;ib<=imail;ib++)
        {ilc++;if(ilc==ilcfin) {cout << "\n erreur de sortie 2 d'animation vrml";Sortie(1);}};
       // fin de la sortie des infos
       sort << *(ilc) << ".set_point";
       }

 //*** le cas des isovaleurs avec plusieurs maillage est à traiter !!!!!!!!!!!!!!
     // si l'on a des isovaleurs on route également sur tous les incréments
     if (isovaleurs_vrml->Actif())
      {sort << "\n \n # ------ cas des isovaleurs, definition des routages pour toutes les deformees ----- ";
       list <string>::iterator ino=list_nom_coordinate.begin();
       ino++; // le premier est déjà fait
       // on boucle sur le nombre d'incrément puis sur le nombre de maillage
       for (int ic=2;ic<= nb_step;ic++) // on commence à deux cas 1 est déjà fait
         for (int im=1;im<=nb_maillage;im++,ino++)
           {sort << "\n ROUTE Clock.fraction_changed TO InterpolDePoints"<<im<<".set_fraction";
            sort << "\n ROUTE InterpolDePoints"<<im<<".value_changed TO " << *(ino)
                 << ".set_point";
            }
       }
     // si l'on a des isovaleurs on définit les routeurs de gestion de la transparence
     // de chaque incrément
     if (isovaleurs_vrml->Actif())
     { sort << "\n \n # ------ cas des isovaleurs, definition des routages pour la transparence "
            << " des matieres ----- ";
       // récup de la liste des noms de matière
       const list <string> & noms_matiere = choix_deformee->Noms_matiere();
       list <string>::const_iterator inom=noms_matiere.begin();
       // on boucle sur le nombre d'incrément puis sur le nombre de maillage
       for (int ic=1;ic<= nb_step;ic++)
         for (int im=1;im<=nb_maillage;im++,inom++)
           {sort << "\n ROUTE Clock.fraction_changed TO inter_intense"<<ic<<".set_fraction";
            sort << "\n ROUTE inter_intense"<<ic<<".value_changed TO " << *(inom)
                 << ".set_transparency";
            }
      }
 //    sort << "\n ROUTE Clock.fraction_changed TO InterpolDeCouleurs.set_fraction";
 //    sort << "\n ROUTE InterpolDeCouleurs.value_changed TO " << nom_base_couleur
 //         << ".set_color";
             
      // et on vide le buffer de sortie
      sort << endl;
     };
  };

};

// choix de l'ordre, cet méthode peut entraîner la demande d'informations
// supplémentaires si nécessaire. qui sont ensuite gérer par la classe elle même
void Animation_vrml::ChoixOrdre()
   { // demande de précision
     bool choix_valide = false;  string rep;
     cout << "\n ----> choix des parametre de l'animation "
             << "\n  parametres actuels ?   : duree de l'animation : " << cycleInterval
             << "\n                        bouclage de l'animation : " ; 
             if (loop) cout << "oui"; else cout << "non";
     cout    << "\n                debut automatique ";
             if (debut_auto) cout << "oui"; else cout << "non";
     cout    << "\n                duree entre deux passages automatiques: " << inter_2pas
             << "\n              (valable si l'on a un debut automatique);";               
     cout << " \n  (rep 'o') pour accepter ces parametres sinon autre ";
     cout << "\n reponse ? "; rep = lect_return_defaut(true,"o");;
   if (rep != "o") 
    {// cas d'un choix autre que standart
     while (!choix_valide)
      {
       try 
        { 
          cout 
          << "\n (0) fin modif"
          << "\n (1) duree du cycle?                      (2)bouclage de l'animation ?  "
          << "\n (3) debut automatique ?                  (4)duree entre deux passages auto? ";
          cout << "\n \n reponse ? ";
          rep = lect_return_defaut(false,"f");
          if (rep == "fin_prog") Sortie(1);
          // sinon
          int num = ChangeEntier(rep);
          if (num == 0) choix_valide=true;
          else if ((num >= 1)&&(num<=4)) 
           { choix_valide=false;
             switch (num)
              { case 1:  //durée du cycle
                { // cas de la durée du cycle 
                  cout << "\n duree du cycle (un reel strictement positif)  ? ";
                  double duree; 
                  duree=lect_double();
                  if (duree > 0) cycleInterval = duree;
                  else 
                    { cout << "erreur le cycle doit être positif!,  on garde la valeur precedente "
                           << cycleInterval;
                     }
                  break;
                 }
                case 2: // bouclage de l'animation
                { // bouclage continue
                  cout << "\n bouclage non continu (oui (rep 'o'(defaut)) sinon non (autre reponse)  ? ";
                  rep = lect_return_defaut(false,"o");
                  if (rep == "o") loop = false;
                  else loop = true;
                  break;
                 }
                case 3: // début automatique
                { // bouclage continue
                  cout << "\n debut automatique (oui (rep 'o'(defaut)) sinon non (autre reponse)  ? ";
                  rep = lect_return_defaut(false,"o");
                  if (rep == "o") debut_auto = true;
                  else debut_auto = false;
                  break;
                 }
                case 4:  // durée entre deux passage automatique
                { cout << "\n duree entre deux passages, dans le cas d'un demarrage automatique"
                       << "\n  (un reel strictement positif)  ? ";
                  double duree; 
                  duree=lect_double();
                  if (duree > 0) inter_2pas = duree;
                  else 
                    { cout << "erreur la duree doit être positive!,  on garde la valeur precedente "
                           << inter_2pas;
                     }
                  break;
                 } 
              }
             } 
          else { cout << "\n Erreur on attendait un entier entre 0 et 4 !!, "
                      << "\n redonnez une bonne valeur"
                      << "\n ou taper fin_prog pour arreter le programme"; 
              choix_valide=false;
             }
         }    
       catch (ErrSortieFinale)
            // cas d'une direction voulue vers la sortie
            // on relance l'interuption pour le niveau supérieur
          { ErrSortieFinale toto;
            throw (toto);
          }
       catch (...)// erreur de lecture
       {  cout << "\n Erreur on attendait un des mots clés proposes !!, "
                      << "\n redonnez une bonne valeur"
                      << "\n ou taper fin_prog pour arreter le programme"; 
              choix_valide=false;
        }
       } //-- fin du while
      } //-- fin du if (rep != "o") du départ c'est-à-dire du cas non standart 
    
       // on prévient qu'il faut absolument renseigner l'ordre "déformée"
       cout << "\n n'oubliez pas de definir les parametres de deformees pour que l'animation fonctionne\n";
                
       // appel de la méthode de la classe mère
       OrdreVisu::ChoixOrdre();    
     };     
            
// initialisation des listes de coordonnées
void Animation_vrml::Init_liste_coordonnee() 
       {OrdreVisu::Mise_zero_coordo(); } ; 

// lecture des paramètres de l'ordre dans un flux
void Animation_vrml::Lecture_parametres_OrdreVisu(UtilLecture & entreePrinc)
 { // si dans le flot il existe l'identificateur adoc on lit sinon on passe
   if (strstr(entreePrinc.tablcarCVisu,"debut_animation")!=NULL)
     {// sauvegarde des paramètres  actuelles
      bool loop_s=loop; bool debut_auto_s=debut_auto; 
      double cycleInterval_s = cycleInterval; double startTime_s=startTime; double stopTime_s=stopTime;
      double inter_2pas_s = inter_2pas; 
      // essaie de lecture
      try 
        { string nom; 
          (*entreePrinc.entCVisu) >> nom ;
          if (nom != "debut_animation")
            { cout << "\n Erreur en lecture des parametres de l'animation a partir d'un fichier .CVisu,"
                   << " le premier enregistrement doit etre le mot clef: debut_animation "
                   << " on ne tiens pas compte  des parametres fournies !! ";
              }
          else
           { // appel de l'ordre de la classe mère
             OrdreVisu::Lect_para_OrdreVisu_general(entreePrinc);
             while (nom != "fin_animation")
               { (*entreePrinc.entCVisu) >> nom;
                 if (nom == "cycleInterval") (*entreePrinc.entCVisu) >> cycleInterval;
                 if (nom == "loop")  (*entreePrinc.entCVisu) >> loop;
                 if (nom == "startTime") (*entreePrinc.entCVisu) >> startTime;
                 if (nom == "stopTime") (*entreePrinc.entCVisu) >> stopTime ;
                 if (nom == "debut_auto") (*entreePrinc.entCVisu) >> debut_auto ;
                 if (nom == "inter_2pas") (*entreePrinc.entCVisu) >> inter_2pas ;
                 entreePrinc.NouvelleDonneeCVisu(); // on passe un enregistrement                  
                }
            }       
         }    
      catch (ErrSortieFinale)
           // cas d'une direction voulue vers la sortie
           // on relance l'interuption pour le niveau supérieur
         { ErrSortieFinale toto;
           throw (toto);
         }
      catch (...)// erreur de lecture
       {  cout << "\n Erreur en lecture des parametres de l'animation a partir d'un fichier .CVisu,"
               << " on ne tiens pas compte  des parametres fournies !! ";
          // récup des infos  sauvées
          cycleInterval=cycleInterval_s; loop=loop_s; 
          startTime = startTime_s; stopTime=stopTime_s; debut_auto=debut_auto_s; inter_2pas=inter_2pas_s;
          if (ParaGlob::NiveauImpression() >= 4)     
             cout  << "\n Animation_vrml::Lecture_parametres_OrdreVisu(..";
        }
      } 
  };

// écriture des paramètres de l'ordre dans un flux
void Animation_vrml::Ecriture_parametres_OrdreVisu(UtilLecture & entreePrinc)
 { // récup du flot  
   ostream & sort = (*(entreePrinc.Sort_CommandeVisu()));
   // on commente le fonctionnement
   sort << "\n #  ----------------------------- definition des parametres d'animation: ---------------- ";
   sort << "\n debut_animation #  un mot cle de debut de liste (debut_animation)";
   // appel de l'ordre de la classe mère
   OrdreVisu::Ecrit_para_OrdreVisu_general(entreePrinc);
   sort << "\n #  des parametres avec des valeurs: (sur une meme ligne) ";
   // la liste des paramètres
   sort << "\n cycleInterval " << cycleInterval << " # cycleInterval  <un reel> (indique le temps en seconde du cycle de l'animation)"
        << "\n loop " << loop << " # loop  <0 ou 1> (indique si l'on veut une animation en boucle continue ou non)"
        << "\n startTime " << startTime << " # startTime  <un reel> (temps de démarrage de l'animation en absolu depuis 1970)"
        << "\n stopTime " << stopTime << " # stopTime  <un réel> (temps de fin en absolu depuis 1970, si < a starttime on n'en tiend pas compte)"
        << "\n debut_auto " << debut_auto << " # debut_auto  <0 ou 1> (indique si l'on veut ou non un debut automatique de l'animation)" 
        << "\n inter_2pas " << inter_2pas << " # inter_2pas  <un réel> (temps entre deux cycle d'animation, si loop est actif)";
   // fin      
   sort << "\n fin_animation " << " #   un mot cle de fin  \n";    
  };