// 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 "F_cycle_add.h"
#include "Sortie.h"
#include "ConstMath.h"
#include "MathUtil.h"
#include "CharUtil.h"


    // CONSTRUCTEURS :
F_cycle_add::F_cycle_add(string nom) :
 Courbe1D(nom,F_CYCLE_ADD),F1(NULL)
 ,ampli(1.),longcycl(ConstMath::tresgrand),decalx(0.),decaly(0.)
  {};
    
// constructeur fonction d'une courbe existante et d'un nom
F_cycle_add::F_cycle_add(Courbe1D* FF1,  string nom):
 Courbe1D(nom,F_CYCLE_ADD),F1(FF1)
 ,nom_courbe1("")
 ,ampli(1.),longcycl(ConstMath::tresgrand),decalx(0.),decaly(0.)
  { // création d'une courbe locale que si elle était déjà locale
    if (FF1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(FF1));}; 
    };
    
    // de copie
F_cycle_add::F_cycle_add(const F_cycle_add& Co) :
 Courbe1D(Co)
 ,F1(Co.F1) // a priori on pointe sur la même courbe interne 
 ,nom_courbe1(Co.nom_courbe1)
 ,ampli(Co.ampli),longcycl(Co.longcycl),decalx(Co.decalx),decaly(Co.decaly)
  { // création d'une courbe locale que si elle était déjà locale
    if (Co.F1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(Co.F1));}; 
    };
    // de copie à partir d'une instance générale
F_cycle_add::F_cycle_add(const Courbe1D& Coo) :
 Courbe1D(Coo)
  { if (Coo.Type_courbe() != F_CYCLE_ADD)
      { cout << "\n erreur dans le constructeur de copie pour une courbe F_CYCLE_ADD "
             << " à partir d'une instance générale ";
        cout << "\n F_cycle_add::F_cycle_add(const Courbe1D& Co) ";
           Sortie(1);          
          };
    // définition des données
    F_cycle_add & Co = (F_cycle_add&) Coo;
    nom_courbe1 = Co.nom_courbe1;
    ampli=Co.ampli;
    longcycl=Co.longcycl;
    decalx=Co.decalx; decaly=Co.decaly;
    // création d'une courbe locale que si elle était déjà locale
    if (Co.F1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(Co.F1));}
    else F1=Co.F1; // sinon cas d'une courbe globale
  };

    // DESTRUCTEUR :
F_cycle_add::~F_cycle_add() 
  { // on efface la courbe que si c'est une courbe locale
    if ((F1 != NULL)&&(nom_courbe1=="i_interne_i")) delete F1;
  };
    
    // METHODES PUBLIQUES :
    
    // --------- virtuelles ---------
    
    // affichage de la courbe
void F_cycle_add::Affiche() const 
  { cout << "\n courbe composee : F_cycle_add : nom_ref= " << nom_ref;
    // si c'est une courbe interne on l'affiche globalement
    // si c'est une courbe globale, on n'affiche que son nom
    cout << "\n fonction de base: ";
    if (F1->NomCourbe() == "_")  {F1->Affiche();}
    else  {cout << F1->NomCourbe() << " ";};
    cout << "\n facteur d'amplification= " << ampli
         << " longueur_cycle= " << longcycl 
         << " decalagex= " << decalx << " decalagey= " << decaly << " " ;
    cout << "\n ----- fin fonction F_cycle_add ----- ";
   };

    // vérification que tout est ok, pres à l'emploi
    // ramène true si ok, false sinon
bool F_cycle_add::Complet_courbe()const
 { bool ret = Complet_var(); // on regarde du coté de la classe mère tout d'abord
   // puis les variables propres
   if (F1 == NULL) ret = false;
   if (!ret && (ParaGlob::NiveauImpression() >0))
      { cout << "\n ***** la courbe n'est pas complete ";
        this->Affiche();
        };
   return ret;
  } ;

// dans le cas où la courbe membre est une  courbe externe
// fonction pour la définir
// la courbe est défini en interne que si la courbe argument est elle même
// une courbe locale. c'est-à-dire si FF1->NomCourbe() ="_" alors on recrée une courbe
// interne avec new pour F1, sinon F=FF1 et pas de création; 
// dans le cas où FF1 ou FF1 est NULL on passe, pas de traitement pour ce pointeur
void F_cycle_add::DefCourbesMembres(Courbe1D* FF1)
{ // création d'une courbe locale que si elle était déjà locales
  if (FF1 != NULL)
   {if (FF1->NomCourbe() == "_") // cas où FF1 est une courbe non globale
      { if (F1==NULL)
         { // cas où la courbe locale n'est pas défini mais on veut une courbe interne
           F1=Courbe1D::New_Courbe1D(*(FF1));nom_courbe1="i_interne_i";
          } 
        else if (F1->NomCourbe() == "_")
          // cas où la courbe F1 est local et on veut la remplacer par une nouvelle locale
          { delete F1;
            F1=Courbe1D::New_Courbe1D(*(FF1));nom_courbe1="i_interne_i";
           }
        else
          // cas où la courbe F1 est global et on veut la remplacer par une locale
          { F1=Courbe1D::New_Courbe1D(*(FF1));nom_courbe1="i_interne_i";
           };
       }
    else // cas ou FF1 est une courbe globale
      { if (F1==NULL)
         { // cas où la courbe locale n'est pas définir
           F1=FF1;nom_courbe1="e_externe_e";
          } 
        else if (F1->NomCourbe() == "_")
          // cas où la courbe F1 est local et on veut la remplacer par une globale
          { delete F1;
            F1=FF1;nom_courbe1="e_externe_e";
           }
        else
          // cas où la courbe F1 est global et on veut la remplacer par une globale
          { F1=FF1;nom_courbe1="e_externe_e";
           };
       };
   }; 

};

   // Lecture des donnees de la classe sur fichier
    // le nom passé en paramètre est le nom de la courbe
    // s'il est vide c-a-d = "", la methode commence par lire le nom sinon
    // ce nom remplace le nom actuel
void F_cycle_add::LectDonnParticulieres_courbes(const string& nom,UtilLecture * entreePrinc)
  { // entête de la courbe
    if (nom == "")  { *(entreePrinc->entree) >> nom_ref;}
    else {nom_ref=nom;};
    // lecture de la  courbe interne
    entreePrinc->NouvelleDonnee();  // lecture d'une nouvelle ligne
    // on lit l'entête
    if(strstr(entreePrinc->tablcar,"courbe1=")==0)
         { cout << "\n erreur en lecture de l'entete  "
                << " on attendait la chaine: courbe1= ";
           entreePrinc->MessageBuffer("**erreur1 F_cycle_add::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
    string toto,nom_lu;      
    *(entreePrinc->entree) >> toto >> nom_lu;    
    // on regarde si la courbe1 existe, si oui on récupère la référence 
    if (Type_EnumCourbe1D_existe(nom_lu)) 
        // cas ou c'est un nom de type de courbe -> lecture directe
      { nom_courbe1 = "_"; // on signale que c'est une courbe interne
        F1 = Courbe1D::New_Courbe1D(nom_courbe1,Id_Nom_Courbe1D (nom_lu.c_str()));
        // lecture de la courbe
        F1->LectDonnParticulieres_courbes (nom_courbe1,entreePrinc);
        nom_courbe1="i_interne_i";
       } 
    else
      // sinon on retiend le nom pour une complétion future   
      {nom_courbe1 = nom_lu;};
    // lecture de l'amplification et de la longueur du cycle
    entreePrinc->NouvelleDonnee();  // lecture d'une nouvelle ligne
    *(entreePrinc->entree) >> nom_lu >> longcycl;
    if (nom_lu != "longueur_cycle_=")
         { cout << "\n erreur en lecture de la longueur du cycle  "
                << " on attendait la chaine: longueur_cycle_= et on a lue " << nom_lu;
           entreePrinc->MessageBuffer("**erreur1 F_cycle_add::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
    if (longcycl <= ConstMath::petit) 
         { cout << "\n ****** attention ****** la longueur de cycle lue vaut:   " << longcycl
                << " c'est une grandeur tres petite, ce qui peut entrainer des erreurs de calcul !! ";
          };
          
    if(strstr(entreePrinc->tablcar,"amplification_=")!=0)
      // cas ou on veut définir un facteur d'amplification
      { *(entreePrinc->entree) >> nom_lu >> ampli;
        if (nom_lu != "amplification_=")
         { cout << "\n erreur en lecture du facteur d'amplification  "
                << " on attendait la chaine: amplification_= et on a lue " << nom_lu;
           entreePrinc->MessageBuffer("**erreur1 F_cycle_add::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
      };
          
    if(strstr(entreePrinc->tablcar,"decalageX_=")!=0)
      // cas ou on veut définir un décalage initiale
      { *(entreePrinc->entree) >> nom_lu >> decalx;
        if (nom_lu != "decalageX_=")
         { cout << "\n erreur en lecture du decalage initiale en x "
                << " on attendait la chaine: decalageX_= et on a lue " << nom_lu;
           entreePrinc->MessageBuffer("**erreur1 F_cycle_add::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
      };
          
    if(strstr(entreePrinc->tablcar,"decalageY_=")!=0)
      // cas ou on veut définir un décalage initiale
      { *(entreePrinc->entree) >> nom_lu >> decaly;
        if (nom_lu != "decalageY_=")
         { cout << "\n erreur en lecture du decalage en y initiale  "
                << " on attendait la chaine: decalageY_= et on a lue " << nom_lu;
           entreePrinc->MessageBuffer("**erreur1 F_cycle_add::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
      };

   };

// 1) renseigne si la courbe dépend d'autre courbe ou non
bool F_cycle_add::DependAutreCourbes() const
 { bool ret=false;
   if(F1 == NULL) {ret=true;}
   else if (nom_courbe1!="i_interne_i") {ret=true;};
   return ret;
  };
  
// 2) retourne une liste de nom correspondant aux noms de courbes dont dépend *this
list <string>& F_cycle_add::ListDependanceCourbes(list <string>& lico) const
 { // tout d'abord on vide la liste passée en paramètres
   if (lico.size() != 0) lico.erase(lico.begin(),lico.end());
   // on remplit en fonction de l'état
   if(F1 == NULL) lico.push_back(nom_courbe1);
   return lico;
  };
   
// 3) établit la connection entre la demande de *this et les courbes passées en paramètres
void F_cycle_add::Lien_entre_courbe (list <Courbe1D *>&  liptco)
 { Courbe1D* FF1=NULL;
   list <Courbe1D *>::iterator ili,ilifin=liptco.end();
   if (F1==NULL)
    { // on cherche la courbe correspondante
      for (ili=liptco.begin();ili!=ilifin;ili++)
        if ((*ili)->NomCourbe() == nom_courbe1) {FF1=(*ili);break;};
     };
   // et on définit les courbes
   this->DefCourbesMembres(FF1);  
  };
    
// def info fichier de commande
void F_cycle_add::Info_commande_Courbes1D(UtilLecture & entreePrinc) 
  {  
     ofstream & sort = *(entreePrinc.Commande_pointInfo()); // pour simplifier
     sort << "\n#............................................"
          << "\n#   il s'agit d'une fonction qui est cyclique, a chaque debut de cycle"
          << "\n#   elle prend la valeur de la fin du cycle precedent + , la valeur d'une "
          << "\n#   fonction de base multipliee par un facteur d'amplitude fois n, "
          << "\n#   n etant le nombre de cycle qui vaut 1 par defaut"
          << "\n#   ainsi on doit indiquer la longueur du cycle, le facteur d'amplitude est "
          << "\n#   facultatif (=1 par defaut). On indique au debut de la declaration, la fonction de base "
          << "\n#   Remarque: le premier cycle commence a x=0. "
          << "\n# *** exemple 1 de definition d'une courbe composee F_CYCLE_ADD  ****" 
          << "\n#   1) ici on indique le nom de la courbe interne qui doit donc etre"
          << "\n#   definis par ailleurs "
          << "\n courbe_exemple1    F_CYCLE_ADD  # nom de la courbe "
          << "\n   courbe1= fonction_temperature   # def  de la courbe interne"
          << "\n   longueur_cycle_= 10  amplification_= 2   "
          << "\n       "
          << "\n#   Il est egalement possible d'introduire un decalage en x et y. Le decalage en x "
          << "\n#   est soustrait a la valeur courante de x, tandis que le decalage en y est ajoute "
          << "\n#   a la valeur finale de la fonction. Exemple de syntaxe "
          << "\n   longueur_cycle_= 10  amplification_= 2  decalageX_= 10. decalageY_= 3. "
          << "\n       "
          << "\n# *** exemple 2 de definition d'une courbe composee F_CYCLE_ADD ****" 
          << "\n#   ici on indique explicitement la courbe interne "
          << "\n#   definis par ailleurs "
          << "\n courbe_exemple2    F_CYCLE_ADD  # nom de la courbe "
          << "\n# def d'une fonction echelon (interne) avec une attenuation des angles "
          << "\n  courbe1= COURBEPOLYLINEAIRE_1_D "  
          << "\n     Debut_des_coordonnees_des_points "
          << "\n       Coordonnee dim= 2 0.  0. "
          << "\n       Coordonnee dim= 2 0.2 0.05 "
          << "\n       Coordonnee dim= 2 0.4 0.2 "
          << "\n       Coordonnee dim= 2 0.6 0.8 "
          << "\n       Coordonnee dim= 2 0.8 0.95 "
          << "\n       Coordonnee dim= 2 1. 1. "
          << "\n       Coordonnee dim= 2 10.  1. "
          << "\n     Fin_des_coordonnees_des_points "
          << "\n# def des parametres internes du cycle "  
          << "\n  longueur_cycle_= 5. "
          << "\n       "
          << endl;
   };
        
    // ramène la valeur 
double F_cycle_add::Valeur(double x)  
  { int n=(x-decalx)/longcycl;
    double resul = decaly + ampli * ((n*n+n)/2. * F1->Valeur(longcycl)
                                    +  (n+1) * F1->Valeur(x-n*longcycl-decalx));
//    cout << "\n cyclique : valeur = " << resul;
    return resul;
  };

    // ramène la valeur et la dérivée en paramètre
Courbe1D::ValDer F_cycle_add::Valeur_Et_derivee(double x)  
  { int n=(x-decalx)/longcycl;
    ValDer ret1 = F1->Valeur_Et_derivee(x-n*longcycl-decalx);
    ValDer ret;   // le retour
    ret.valeur = decaly + ampli * ((n*n+n)/2. * F1->Valeur(longcycl)
                                   +  (n+1) * ret1.valeur);
    ret.derivee = ampli * (n+1) * ret1.derivee;               
    return  ret;
   };
    
    // ramène la dérivée 
double F_cycle_add::Derivee(double x)  
  { int n=(x-decalx)/longcycl;
    double derivee = F1->Derivee(x-n*longcycl-decalx);
    return (ampli * (n+1) * derivee);
   };    
        
    // ramène la valeur et les dérivées première et seconde en paramètre
Courbe1D::ValDer2 F_cycle_add::Valeur_Et_der12(double x)
  { int n=(x-decalx)/longcycl;
    ValDer2 ret1 = F1->Valeur_Et_der12(x-n*longcycl-decalx);
    ValDer2 ret;   // le retour
    ret.valeur = decaly + ampli * ((n*n+n)/2. * F1->Valeur(longcycl)
                                   +  (n+1) * ret1.valeur);
    ret.derivee = ampli * (n+1) * ret1.derivee;               
    ret.der_sec = ampli * (n+1) * ret1.der_sec;               
    return  ret;
   };
        
    // ramène la dérivée seconde
double F_cycle_add::Der_sec(double x)
  { int n=(x-decalx)/longcycl;
    double derivee_seconde = F1->Der_sec(x-n*longcycl-decalx);
    return (ampli * (n+1) * derivee_seconde);
   };    
    
    // ramène la valeur si dans le domaine strictement de définition
    // si c'est inférieur au x mini, ramène la valeur minimale possible de y
    // si supérieur au x maxi , ramène le valeur maximale possible de y
// en fait ici il s'agit des bornes éventuelles pour la fonction F1
// car la fonction cycle n'a pas de borne
Courbe1D::Valbool  F_cycle_add::Valeur_stricte(double x)  
  { int n=(x-decalx)/longcycl;
    Valbool ret1 = F1->Valeur_stricte(x-n*longcycl-decalx);   
    Valbool ret2 = F1->Valeur_stricte(longcycl); 
    Valbool ret; // le retour
    ret.valeur = decaly + ampli * ((n*n+n)/2. * ret2.valeur
                                   +  (n+1) * ret1.valeur);
    ret.dedans = ret1.dedans && ret2.dedans ;
    return ret;
  };
    
    // ramène la valeur et la dérivée si dans le domaine strictement de définition
    // si c'est inférieur au x mini, ramène la valeur minimale possible de y et Y' correspondant
    // si supérieur au x maxi , ramène le valeur maximale possible de y et Y' correspondant
// en fait ici il s'agit des bornes éventuelles pour la fonction F1
// car la fonction cycle n'a pas de borne
Courbe1D::ValDerbool  F_cycle_add::Valeur_Et_derivee_stricte(double x)  
  { int n=(x-decalx)/longcycl;
    ValDerbool ret1 = F1->Valeur_Et_derivee_stricte(x-n*longcycl-decalx);   
    Valbool ret2 = F1->Valeur_stricte(longcycl); 
    ValDerbool ret; // le retour
    ret.valeur = decaly + ampli * ((n*n+n)/2. * ret2.valeur
                                   +  (n+1) * ret1.valeur);
    ret.derivee = ampli * (n+1) * ret1.derivee;
    ret.dedans = ret1.dedans && ret2.dedans ;
    return  ret;
   };
    
	//----- lecture écriture de restart -----
	// cas donne le niveau de la récupération
    // = 1 : on récupère tout
    // = 2 : on récupère uniquement les données variables (supposées comme telles)
void F_cycle_add::Lecture_base_info(istream& ent,const int cas)
  { // on n'a que des grandeurs constantes
    if (cas == 1)
      { string nom;
        // lecture et vérification de l'entête
        ent >> nom;
        if (nom != "F_CYCLE_ADD")
         { cout << "\n erreur dans la verification du type de courbe lue ";    
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           } 
        // lecture des infos 
        string nom1,nom2,nom3;
        // pour la courbe1
        ent >> nom1 >> nom2 >> nom3;
        if (nom1 != "courbe1=")
         { cout << "\n erreur dans la verification du type, on attendait le mot cle courbe1= "
                << " et on a lu " << nom1 << " ";
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           }
        else
         { if (nom2 == "COURBE_INTERNE1")
            {// cas d'une courbe en interne
             // 1) on commence par effacer la courbe existante si nécessaire
             if (F1 != NULL) {if (F1->NomCourbe() == "_") delete F1;};
             // 2) on crée la courbe adoc
             nom2="_";
             F1 = Courbe1D::New_Courbe1D(nom2,Id_Nom_Courbe1D (nom3.c_str()));
             // 3) on lit les données particulières
             F1->Lecture_base_info(ent,cas);
             nom_courbe1="i_interne_i";
             }
           else
            {// cas d'une courbe externe on lit le nom
             nom_courbe1 = nom2; 
             };
          };    
        // amplification et longueur du cycle  
        ent >>  nom1  >> ampli >> nom2 >> longcycl ;
        if (nom1 != "amplification_=")
         { cout << "\n erreur dans la lecture du facteur d'amplification on attendait amplification_= "
                << " et on a lu " << nom1 << " ";
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           };
        if (nom2 != "longueur_cycle_=")
         { cout << "\n erreur dans la lecture de la longueur du cycle on attendait longueur_cycle_= "
                << " et on a lu " << nom2 << " ";
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           };
        ent >>  nom1  >> decalx >> nom2 >> decaly ;
        if (nom1 != "decalageX_=")
         { cout << "\n erreur dans la lecture de la longueur du cycle on attendait decalageX_= "
                << " et on a lu " << nom1 << " ";
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           };
        if (nom2 != "decalageY_=")
         { cout << "\n erreur dans la lecture de la longueur du cycle on attendait decalageY_= "
                << " et on a lu " << nom2 << " ";
           cout << "\n F_cycle_add::Lecture_base_info(... ";
           Sortie(1); 
           };
      };
   };

    // cas donne le niveau de sauvegarde
    // = 1 : on sauvegarde tout
    // = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void F_cycle_add::Ecriture_base_info(ostream& sort,const int cas)
  { // on n'a que des grandeurs constantes
    if (cas == 1)
      { sort << " F_CYCLE_ADD  ";
        if (F1->NomCourbe() == "_")
         {// cas d'une courbe interne
          sort << "\n courbe1=  COURBE_INTERNE1 " << F1->Type_courbe();
          F1->Ecriture_base_info(sort,cas);
          }
        else
          // cas d'une courbe externe
          {sort << "\n courbe1= " <<  F1->NomCourbe() << " " << F1->Type_courbe();;};
        // amplification et longueur du cycle et décalage 
        sort << "\n amplification_= " << ampli
             << " longueur_cycle_= " << longcycl 
             << " decalageX_= " << decalx << " decalageY_= " << decaly <<  " " ;

       };
   };
         
// sortie du schemaXML: en fonction de enu 
//void F_cycle_add::SchemaXML_Courbes1D(ostream& sort,const Enum_IO_XML enu)
void F_cycle_add::SchemaXML_Courbes1D(ostream& ,const Enum_IO_XML enu)
  {
	switch (enu)
	{ case XML_TYPE_GLOBAUX :
		{
		 break;
		}
		case XML_IO_POINT_INFO :
		{
		 break;
		}
		case XML_IO_POINT_BI :
		{
		 break;
		}
		case XML_IO_ELEMENT_FINI :
		{
		 break;
		}
	};		
  };