// 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 "Increment_vrml.h"

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

    // CONSTRUCTEURS :
// par defaut
Increment_vrml::Increment_vrml () :
   OrdreVisu(".........................choix numeros d'increment"  // de 15 à 65=50 caracteres
             ,"definition d'un numero d'increment ou d'une liste de numero","cni")
   ,lis_inc(NULL),lis_sor()
     { // par défaut on active cet ordre avec l'incrément initial qui normalement existe
       lis_sor.push_back(0);
 //        cout << "\n liste actuelle: debug ";
 //        list <int>::iterator ikk,ikkfin=lis_sor.end();
 //        for (ikk=lis_sor.begin();ikk!=ikkfin;ikk++) cout << *ikk << " ";
 //        cout << "\n fin *** liste actuelle: debug ";

       OrdreVisu::ChoixOrdre();
       }; 
     
     // constructeur de copie 
Increment_vrml::Increment_vrml (const Increment_vrml& ord) :
    OrdreVisu(ord),lis_inc(ord.lis_inc),lis_sor(ord.lis_sor)
      {
      
      };
               
    // DESTRUCTEUR :
Increment_vrml::~Increment_vrml () 
     {};  
    
    // METHODES PUBLIQUES :

// choix de l'ordre
void Increment_vrml::ChoixOrdre()
   { // demande de précision
     bool choix_valide = false;
     list<int>::iterator ik,ikfin=lis_inc->end();
     // on traite tout d'abord le cas où il n'y a pas d'incrément à visualiser
     // dans ce dernier cas on arrête le traitement interactif
     if(lis_inc->begin() == ikfin)
       { cout << "\n *** pas d'increment disponible en dehors de l'increment 0 mis par defaut !! ";
         return;
        } 
     // puis cas où il existe des incréments non nul
     while (!choix_valide)
     {try 
      { cout << "\n liste actuelle: ";
        list <int>::iterator ikk,ikkfin=lis_sor.end();
        for (ikk=lis_sor.begin();ikk!=ikkfin;ikk++) cout << *ikk << " ";
        cout << "\n liste des increments disponibles (en plus de l'increment 0 mis par defaut): ";
        for (ik=lis_inc->begin();ik!=ikfin;ik++)
           cout << *ik << " ";
        cout << "\n parametre par defaut ? le dernier increment (rep 'o') pour accepter "
             << "\n                                             (rep 'ac') pour laisser en l'etat"
             << "\n                                             (sinon autre) \n";
        string rep;
        rep = lect_return_defaut(false,"o");
        if (rep == "ac")
         { // on laisse en l'etat
           cout << "\n aucun changement "; 
           choix_valide = true;
          }
        else if (rep != "o") 
          { 
            // On regarde tout d'abord si l'on veut des incréments en plus ou si l'on ne veut que 
            // les incréments  que l'on va indiquer
            cout << "\n voulez-vous effacer la liste actuelle qui est : ";
            list <int>::iterator ik,ikfin=lis_sor.end();
            for (ik=lis_sor.begin();ik!=ikfin;ik++) cout << *ik << " ";
            cout << "\n rep 'o' pour effacer sinon autre (defaut)? ";
            rep = lect_return_defaut(false,"n");
            if (rep == "o") lis_sor.erase(lis_sor.begin(),lis_sor.end());
                      
            // définition des incréments            
            cout << "\n differentes options : "
                 << "\n un increment                               rep : 1 "
                 << "\n un interval d'increments par pas de 1      rep : 2 "
                 << "\n un interval d'increments avec choix du pas "
                 << "\n entre deux increment choisit               rep : 3 "
                 << "\n une liste d'increments                     rep : 4 "
                 << "\n tous les increments                        rep : 5 "
                 << "\n tous les increments sans l'increment 0     rep : 6 "
                 << "\n le dernier increment   (defaut)            rep : 7 ";
            rep = lect_return_defaut(false,"7");
            if (rep != "7")
              cout << "\n un premier chiffre negatif indique que c'est un increment a retirer (si possible)"
                   << "\n par contre s'il est positif c'est un increment a ajouter (s'il n'existe pas deja)"
                   << "\n les increments sont systematiquement ordonne en fin de traitement ";
            if (rep== "1")
              {  cout << "\n numero d'increment : ";
                 int irep; irep=(int)lect_double();
                 // on vérifie que l'incrément demandé est possible
                 ik = find(lis_inc->begin(),lis_inc->end(),abs(irep));
                 if (ik != lis_inc->end()) // cas ou l'incrément existe 
                  {if (irep <= 0) 
                     lis_sor.remove(abs(irep)); // suppression s'il existe
                   else  
                     lis_sor.push_back(irep);
                   choix_valide = true;
                   }
               }
            else if (rep== "2")
              {  cout << "\n increment de depart et increment de fin (deux entiers) : ";
                 int irep1,irep2; cin >> irep1 >> irep2;
                 std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );// purge de cin
                 // on repère les deux bornes dans la liste
                 list<int>::iterator idep,ifin;
                 idep = find(lis_inc->begin(),lis_inc->end(),abs(irep1));
                 ifin = find(lis_inc->begin(),lis_inc->end(),abs(irep2));
                 
                 if (!((idep!= lis_inc->end())&&(ifin!= lis_inc->end())))
                   { cout << "erreur de borne (inexistantes ou pas dans le bon ordre ?) ";
                     choix_valide = false;
                    }
                 else
                   { // on met à jour
                     ifin++; 
                     for (ik=idep;ik!=ifin;ik++)
                       {if (irep1 >= 0)
                          lis_sor.push_back(*ik);
                        else
                          lis_sor.remove(*ik); // suppression s'il existe
                        }     
                     choix_valide = true;
                    } 
               }
            else if (rep== "3")
              {  cout << "\n increment de depart et increment de fin (deux entiers) : ";
                 int irep1,irep2; cin >> irep1 >> irep2;
                 std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );// purge de cin
                 cout << "\n pas entre deux increments consecutifs (un entier) : ";
                 int pas_inc; pas_inc=(int)lect_double(); pas_inc = abs(pas_inc);
                 // on repère les deux bornes dans la liste
                 list<int>::iterator idep,ifin;
                 idep = find(lis_inc->begin(),lis_inc->end(),abs(irep1));
                 ifin = find(lis_inc->begin(),lis_inc->end(),abs(irep2));
                 
                 if (!((idep!= lis_inc->end())&&(ifin!= lis_inc->end())))
                   { cout << "erreur de borne (inexistantes ou pas dans le bon ordre ?) ";
                     choix_valide = false;
                    }
                 else
                   { // on positionne la fin suivant le standard stl
                     ifin++; 
                     // init de ik
                     ik = idep;
                     // traitement
                     while(ik!=ifin) 
                      { // enregistrement ou effacement
                        if (irep1 >= 0)
                          lis_sor.push_back(*ik);
                        else
                          lis_sor.remove(*ik); // suppression s'il existe
                        // incrémentation du pointeur d'incrément  
                        for (int i=1;i<= pas_inc;i++)
                          {if (ik != ifin) 
                            ik++;}
                        // on a atteind la borne sup arrêt d'incrémentation
                       }
                     choix_valide = true;
                    } 
               }
            else if (rep == "4")
              {  cout << "\nl'entree d'une liste d'increment s'effectue numero par numero ";
                 int irep = -1; float x_ent;
                 do 
                   { cout << "\n un  numero ?  (un nombre flottant (ex 1.2)pour finir) : ";
                     x_ent=(float)lect_double(); irep = (int) Abs(x_ent);
                     // vérification si le numéro est acceptable
                     list<int>::iterator idep;
                     idep = find(lis_inc->begin(),lis_inc->end(),abs(irep));
                     if ((idep== lis_inc->end()))
                       { cout << "\n numero non acceptable cf la liste : ";
                         for (ik=lis_inc->begin();ik!=ikfin;ik++)
                           cout << *ik << " ";
                         cout << "\n recommencez ";
                         irep = -20;
                        }
                     else
                       { if (irep >= 0)
                            lis_sor.push_back(irep); 
                         else
                            lis_sor.remove(abs(irep)); // suppression s'il existe    
                         choix_valide = true;
                         }       
                     } while (x_ent == (float) irep);
                 }     
            else if (rep == "5")
              {  list<int>::iterator idepfin = lis_sor.end();
                 list<int>::iterator idepdebut = lis_sor.begin();
                 // on commence par effacer la liste actuelle
                 lis_sor.erase(idepdebut,idepdebut);
                 // on rempli la liste finale
                 lis_sor=(*lis_inc);
                 choix_valide = true;
                 }     
            else if (rep == "6")
              {  list<int>::iterator idepfin = lis_sor.end();
                 list<int>::iterator idepdebut = lis_sor.begin();
                 // on commence par effacer la liste actuelle
                 lis_sor.erase(idepdebut,idepfin);
                 // on rempli la liste finale
                 lis_sor=(*lis_inc);
                 // on supprime le premier
                 idepdebut = lis_sor.begin();
                 lis_sor.erase(idepdebut);
                 choix_valide = true;
                 }     
            else if (rep == "7")
              {  // on commence par effacer la liste actuelle
                 lis_sor.erase(lis_sor.begin(),lis_sor.end());
                 // on ajoute l'incrément finale
                 list<int>::const_iterator idepdernier = lis_inc->end();idepdernier--;
                 lis_sor.push_back(*idepdernier);
                 choix_valide = true;
                 }     
            else 
              { cout << "\n choix non valide";
                choix_valide = false;
               }    
            }
        else
         { // cas ou l'on retiend l'incrément par défaut c'est-à-dire le dernier
           // on regarde s'il est déjà enregistré sinon on l'ajoute à la liste de sortie
           list <int>::iterator ider = ikfin; ider--; int derIncr= *ider;
           list<int>::iterator ipos = find(lis_sor.begin(),lis_sor.end(),derIncr);
           if (ipos == lis_sor.end())
             lis_sor.push_back(derIncr);
           choix_valide = true;
           } 
       } //-- fin du try
      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 increments, recommencer";
          choix_valide = false;
        }
       } // -- fin du while
       // on ordonne la liste puis on supprime les doublons enfin on l'afficher
       Propre_liste(lis_sor);
       // appel de la méthode de la classe mère
       OrdreVisu::ChoixOrdre();    
     };
          
// initialisation de la liste à l'incrément 0 et au dernier increment
// s'il est différent
void Increment_vrml::Init_list_inc_DebuEtFin()
  { // on efface la liste actuelle
    lis_sor.erase(lis_sor.begin(),lis_sor.end());
    lis_sor.push_back(0);
    if (lis_inc != NULL)
      { list <int>::iterator ilifin=lis_inc->end();
        if (*ilifin != 0) lis_sor.push_back(*ilifin);
      };
   };   
    
// lecture des paramètres de l'ordre dans un flux
void Increment_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_list_increment")!=NULL)
     {// sauvegarde de la liste actuelle
      list <int> liste_sauve = lis_sor;     
      // essaie de lecture
      try 
        { string nom; 
          (*entreePrinc.entCVisu)  >> nom ;
          if (nom != "debut_list_increment")
            { cout << "\n Erreur en lecture des increments a partir d'un fichier .CVisu,"
                   << " le premier enregistrement doit etre le mot clef: debut_list_increment "
                   << " on ne tiens pas compte  de la liste fournie !! ";
              }
          else
           {// appel de l'ordre de la classe mère
            OrdreVisu::Lect_para_OrdreVisu_general(entreePrinc);
            // on efface la liste actuelle
            lis_sor.erase(lis_sor.begin(),lis_sor.end());
            // maintenant on n'intervient que s'il y a une liste d'incrément disponible
            if (lis_inc != NULL)
             {// lecture de la liste demandée
             if (strstr(entreePrinc.tablcarCVisu,"tous_les_increments_moins_zero")!=NULL)
              { // cas où on veut tous les incréments
                lis_sor = (*lis_inc);
                if ((lis_sor.size() != 0) && (*(lis_sor.begin()) == 0))
                   lis_sor.erase(lis_sor.begin());
               }   
             else if (strstr(entreePrinc.tablcarCVisu,"tous_les_increments")!=NULL)
              { // cas où on veut tous les incréments disponibles
                lis_sor = (*lis_inc);
               }
             else if (strstr(entreePrinc.tablcarCVisu,"dernier_increment")!=NULL)
              { // cas où on veut le dernier incrément
                list <int>::iterator iincre = lis_inc->end();
                if (lis_inc->size() != 0)
                   { iincre--;
                     lis_sor.push_back(*iincre);
                   };
               }
             else
              { // cas de la lecture d'une liste différentes de tous les incréments   
                int compteur=0; // pour éviter une boucle infinie
                int nb_lue_sur_ligne = 0;
                while (compteur < 1000000)
                 { (*entreePrinc.entCVisu) >> nom; compteur++;nb_lue_sur_ligne++;
                   if (nom != "fin_list_increment")
                     { // on vérifie que l'incrément demandé est possible
                       int inc=ChangeEntier(nom);
                       list <int>::iterator ik = find(lis_inc->begin(),lis_inc->end(),inc);
                       if (ik != lis_inc->end()) // cas ou l'incrément existe 
                        { lis_sor.push_back(inc);}
                       else
                        { cout << "\n Erreur en lecture de l'increments "<< nom <<" il n'appartient pas a la liste"
                               << " d'increment possible ON N'EN TIENT PAS COMPTE !! ";}
                       if (nb_lue_sur_ligne > 50) 
                         { nb_lue_sur_ligne=0;entreePrinc.NouvelleDonneeCVisu();}
                      }
                   else break;
                  }
                }//-- fin de la lecture d'une liste différentes de tous les incréments
             }
            else // sinon on met l'incrément 0 par défaut
             {lis_sor.push_back(0);
             };
            };
         }    
      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 increments a partir d'un fichier .CVisu,"
               << " on ne tiens pas compte  de la liste fournie !! ";
          lis_sor=liste_sauve; // récup de la liste sauvée
          if (ParaGlob::NiveauImpression() >= 4)     
             cout  << "\n Increment_vrml::Lecture_parametres_OrdreVisu(..";
        }
      // on passe à un nouvel enregistrement
      entreePrinc.NouvelleDonneeCVisu();
      }  
  };

// écriture des paramètres de l'ordre dans un flux
void Increment_vrml::Ecriture_parametres_OrdreVisu(UtilLecture & entreePrinc)
 { // récup du flot  
   ostream & sort = (*(entreePrinc.Sort_CommandeVisu()));
   // on commente le fonctionnement
   sort << "\n #  ----------------------------- definition de la liste des increments a balayer: ---------------- "
        << "\n debut_list_increment #  un mot cle de debut de liste ";
   // appel de l'ordre de la classe mère
   OrdreVisu::Ecrit_para_OrdreVisu_general(entreePrinc);
   // puis la liste des incréments
   sort << "\n # une liste d'entier separee par des blancs, ou le mot cle (tous_les_increments) "
        << "\n #   un mot cle de fin de liste ( fin_list_increment)"
        << "\n";
   bool cas_simple=false; 
   if ( lis_sor == (*lis_inc))
    { sort << " tous_les_increments   "; cas_simple=true;    
     }
   // on regarde si c'est tous-les-incréments sans le premier incrément
   // on crée une liste intermédiaire de travail
   list <int> lis_sor_inter = lis_sor; 
   lis_sor_inter.push_front(*(lis_inc->begin()));// on rajoute le premier à la liste inter  
   if ( lis_sor_inter == (*lis_inc))
    { sort << " tous_les_increments_moins_zero   "; cas_simple=true;    
     }
   // on regarde si ce n'est pas uniquement le dernier incrément
   if (lis_sor.size() > 0) // on ne regarde que si la liste n'est pas vide
    { list <int>::iterator iincre = lis_sor.end();iincre--;
      if ((lis_sor.size() == 1) && (*(lis_sor.begin())== *iincre))
        {sort << " dernier_increment   "; cas_simple=true; } 
    };
   if(!cas_simple)
    { // cas ou la liste est différente de la liste complète
      // on sort la liste des incréments à balayer, par paquet de 100 maxi sur chaque ligne
      list<int>::const_iterator ili,ilifin=lis_sor.end();
      int compteur=0;
      for (ili=lis_sor.begin();ili!=ilifin;ili++,compteur++)
         { sort << " " << (*ili); if (compteur > 50) {compteur = 0; sort << "\n";}}
     }    
   sort << " fin_list_increment \n";    
  };