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


    // CONSTRUCTEURS :
F1_rond_F2::F1_rond_F2(string nom) :
 Courbe1D(nom,F1_ROND_F2),F1(NULL),F2(NULL)
  {};
    
// constructeur fonction de deux courbes existantes et d'un nom
F1_rond_F2::F1_rond_F2(Courbe1D* FF1, Courbe1D* FF2, string nom):
 Courbe1D(nom,F1_ROND_F2),F1(FF1),F2(FF2)
 ,nom_courbe1(""),nom_courbe2("")
  { // création de courbes locales que si elles étaient déjà locales
    if (FF1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(FF1));}; 
    if (FF2->NomCourbe() == "_") 
         {F2=Courbe1D::New_Courbe1D(*(FF2));}; 
    };
    
    // de copie
F1_rond_F2::F1_rond_F2(const F1_rond_F2& Co) :
 Courbe1D(Co)
 ,F1(Co.F1) // a priori on pointe sur les mêmes courbes internes 
 ,F2(Co.F2) // a priori on pointe sur les mêmes courbes internes 
 ,nom_courbe1(Co.nom_courbe1),nom_courbe2(Co.nom_courbe2)
  { // création de courbes locales que si elles étaient déjà locales
    if (Co.F1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(Co.F1));}; 
    if (Co.F2->NomCourbe() == "_") 
         {F2=Courbe1D::New_Courbe1D(*(Co.F2));}; 
    };
    // de copie à partir d'une instance générale
F1_rond_F2::F1_rond_F2(const Courbe1D& Coo) :
 Courbe1D(Coo)
  { if (Coo.Type_courbe() != F1_ROND_F2)
      { cout << "\n erreur dans le constructeur de copie pour une courbe F1_ROND_F2 "
             << " à partir d'une instance générale ";
        cout << "\n F1_rond_F2::F1_rond_F2(const Courbe1D& Co) ";
           Sortie(1);          
          };
    // définition des données
    F1_rond_F2 & Co = (F1_rond_F2&) Coo;
    nom_courbe1 = Co.nom_courbe1;
    nom_courbe2 = Co.nom_courbe2;
    // création de courbes locales que si elles étaient déjà locales
    if (Co.F1->NomCourbe() == "_") 
         {F1=Courbe1D::New_Courbe1D(*(Co.F1));}
    else F1=Co.F1; // sinon cas d'une courbe globale
    if (Co.F2->NomCourbe() == "_") 
         {F2=Courbe1D::New_Courbe1D(*(Co.F2));} 
    else F2=Co.F2; // sinon cas d'une courbe globale
  };

    // DESTRUCTEUR :
F1_rond_F2::~F1_rond_F2() 
  { // on efface les courbes que si c'est des courbes locales
    if ((F1 != NULL)&&(nom_courbe1=="i_interne_i")) delete F1;
    if ((F2 != NULL)&&(nom_courbe2=="i_interne_i")) delete F2;};
    
    // METHODES PUBLIQUES :
    
    // --------- virtuelles ---------
    
    // affichage de la courbe
void F1_rond_F2::Affiche() const 
  { cout << "\n courbe composee : F1_rond_F2 : 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 premiere fonction: ";
    if (F1->NomCourbe() == "_")  {F1->Affiche();}
    else  {cout << F1->NomCourbe() << " ";};
    cout << "\n seconde fonction: "; 
    if (F2->NomCourbe() == "_")  {F2->Affiche();}
    else  {cout << F2->NomCourbe() << " ";};
    cout << "\n ----- fin fonction F1_rond_F2 ----- ";
   };

    // vérification que tout est ok, pres à l'emploi
    // ramène true si ok, false sinon
bool F1_rond_F2::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 (F2 == NULL) ret = false;
   if (!ret && (ParaGlob::NiveauImpression() >0))
      { cout << "\n ***** la courbe n'est pas complete ";
        this->Affiche();
        };
   return ret;
  } ;

// dans le cas où les courbes membres sont des courbes externes
// fonction pour les définir
// les courbes sont défini en interne que si les courbes arguments sont elles même
// des courbes locales. c'est-à-dire si FF1->NomCourbe() ="_" alors on recrée une courbe
// interne avec new pour F1, sinon F1=FF1 et pas de création; idem pour f2
// dans le cas où FF1 ou FF1 est NULL on passe, pas de traitement pour ce pointeur
void F1_rond_F2::DefCourbesMembres(Courbe1D* FF1, Courbe1D* FF2)
{ // création de courbes locales que si elles étaient 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éfinie
           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";
           };
       };
      }; 
  if (FF2 != NULL)
   {if (FF2->NomCourbe() == "_")  // cas où la courbe FF2 est non globale
      { if (F2==NULL)
         { // cas où la courbe locale n'est pas définie
           F2=Courbe1D::New_Courbe1D(*(FF2));nom_courbe2="i_interne_i";
          } 
        else if (F2->NomCourbe() == "_")
          // cas où la courbe F2 est local et on veut la remplacer par une nouvelle locale
          { delete F2;
            F2=Courbe1D::New_Courbe1D(*(FF2));nom_courbe2="i_interne_i";
           }
        else
          // cas où la courbe F2 est global et on veut la remplacer par une locale
          { F2=Courbe1D::New_Courbe1D(*(FF2));nom_courbe2="i_interne_i";
           };
       }
    else // cas où la courbe FF2 est globale
      { if (F2==NULL)
         { // cas où la courbe locale n'est pas définie
           F2=FF2;nom_courbe2="e_externe_e";
          } 
        else if (F2->NomCourbe() == "_")
          // cas où la courbe F2 est local et on veut la remplacer par une globale
          { delete F2;
            F2=FF2;nom_courbe2="e_externe_e";
           }
        else
          // cas où la courbe F2 est global et on veut la remplacer par une globale
          { F2=FF2;nom_courbe2="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 F1_rond_F2::LectDonnParticulieres_courbes(const string& nom,UtilLecture * entreePrinc)
  { // entête de la courbe
    if (nom == "")  { *(entreePrinc->entree) >> nom_ref;}
    else {nom_ref=nom;};
    // lecture des courbes internes
    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 F1_rond_F2::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;F1 = NULL;};
    // puis la seconde courbe
    entreePrinc->NouvelleDonnee();  // lecture d'une nouvelle ligne
    // on lit l'entête
    if(strstr(entreePrinc->tablcar,"courbe2=")==0)
         { cout << "\n erreur en lecture de l'entete  "
                << " on attendait la chaine: courbe2= ";
           entreePrinc->MessageBuffer("**erreur1 F1_rond_F2::LectureDonneesParticulieres**");
           throw (UtilLecture::ErrNouvelleDonnee(-1));
           Sortie(1);          
          };
    *(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_courbe2 = "_"; // on signale que c'est une courbe interne
        F2 = Courbe1D::New_Courbe1D(nom_courbe2,Id_Nom_Courbe1D (nom_lu.c_str()));
        // lecture de la courbe
        F2->LectDonnParticulieres_courbes (nom_courbe2,entreePrinc);
        nom_courbe2="i_interne_i";
       } 
    else
      // sinon on retiend le nom pour une complétion future   
      {nom_courbe2 = nom_lu;F2 = NULL;};
   };

// 1) renseigne si la courbe dépend d'autre courbe ou non
bool F1_rond_F2::DependAutreCourbes() const
 { bool ret=false;
   if(F1 == NULL) {ret=true;}
//   else if (nom_courbe1!="i_interne_i") {ret=true;};
   if(F2 == NULL) {ret=true;}
//   if (nom_courbe2!="i_interne_i") {ret=true;};
   return ret;
  };
  
// 2) retourne une liste de nom correspondant aux noms de courbes dont dépend *this
list <string>& F1_rond_F2::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)
    { if (nom_courbe1 != "i_interne_i")
       {lico.push_back(nom_courbe1);} // cas normal
      else
       {cout << "\n *** erreur, la courbe 1 est declaree interne, mais n'est pas definie "
             << " ce n'est pas normale, la suite est impossible *** "
             << flush;
        Sortie(1);
       };
    }
   if(F2 == NULL)
    { if (nom_courbe2 != "i_interne_i")
         {lico.push_back(nom_courbe2);} // cas normal
      else
         {cout << "\n *** erreur, la courbe 2 est declaree interne, mais n'est pas definie "
               << " ce n'est pas normale, la suite est impossible *** "
               << flush;
          Sortie(1);
         };
    };
   return lico;
  };
   
// 3) établit la connection entre la demande de *this et les courbes passées en paramètres
void F1_rond_F2::Lien_entre_courbe (list <Courbe1D *>&  liptco)
 { Courbe1D* FF1=NULL; Courbe1D* FF2 = 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;};
     };
   if (F2==NULL)
    { // on cherche la courbe correspondante
      for (ili=liptco.begin();ili!=ilifin;ili++)
        if ((*ili)->NomCourbe() == nom_courbe2) {FF2=(*ili);break;};
     };
   // et on définit les courbes
   this->DefCourbesMembres(FF1,FF2);  
  };
    
// def info fichier de commande
void F1_rond_F2::Info_commande_Courbes1D(UtilLecture & entreePrinc) 
  {  
     ofstream & sort = *(entreePrinc.Commande_pointInfo()); // pour simplifier
     sort << "\n#............................................"
          << "\n# *** exemple 1 de definition d'une courbe composee F1_ROND_F2  ****" 
          << "\n#   ici on indique les noms des courbes internes qui doivent donc etre"
          << "\n#   definis par ailleurs "
          << "\n courbe_exemple1    F1_ROND_F2  # nom de la courbe "
          << "\n   courbe1= fonction_temperature   # def  de la premiere courbe"
          << "\n   courbe2= transfo1               # def  de la seconde courbe"
          << "\n       "
          << "\n# *** exemple 2 de definition d'une courbe composee F1_ROND_F2 ****" 
          << "\n#   ici on indique explicitement les courbes "
          << "\n#   definis par ailleurs "
          << "\n courbe_exemple2    F1_ROND_F2  # nom de la courbe "
          << "\n   courbe1= COURBE_UN_MOINS_COS   # def  de la premiere courbe"
          << "\n      # def des coeff de la courbe demi sinus f(t)= c/2*(1-cos((x-a)*Pi/(b-a)))"
          << "\n      # pour x < a => f=0, pour x>b => f=c"
          << "\n      a= 0. b= 1. c= 1."
          << "\n   courbe2= COURBEPOLYNOMIALE               # def  de la seconde courbe"
          << "\n      # def des coefficients  d'un polynome du troisieme degre 1+3x+6x^2+8x^3"
          << "\n      debut_coef=  1.   3.   6.  8.  fin_coef "
          << "\n       "
          << endl;
   };
        
    // ramène la valeur 
double F1_rond_F2::Valeur(double x)  
  { return F1->Valeur(F2->Valeur(x));};

    // ramène la valeur et la dérivée en paramètre
Courbe1D::ValDer F1_rond_F2::Valeur_Et_derivee(double x)  
  {  ValDer ret1 = F2->Valeur_Et_derivee(x);
     ValDer ret2 = F1->Valeur_Et_derivee(ret1.valeur);
     ValDer ret; ret.valeur=ret2.valeur; ret.derivee=ret1.derivee*ret2.derivee;
     return  ret;
   };
    
    // ramène la dérivée 
double F1_rond_F2::Derivee(double x)  
  {  ValDer ret1 = F2->Valeur_Et_derivee(x);
     ValDer ret2 = F1->Valeur_Et_derivee(ret1.valeur);
     return  ret1.derivee*ret2.derivee;
   };
        
    // ramène la valeur et les dérivées première et seconde en paramètre
Courbe1D::ValDer2 F1_rond_F2::Valeur_Et_der12(double x)
  {  ValDer2 ret1 = F2->Valeur_Et_der12(x);
     ValDer2 ret2 = F1->Valeur_Et_der12(ret1.valeur);
     ValDer2 ret; ret.valeur=ret2.valeur; ret.derivee=ret1.derivee*ret2.derivee;
     ret.der_sec = ret1.der_sec * ret2.derivee + Sqr(ret1.derivee) * ret2.der_sec;
     return  ret;
   };
        
    // ramène la dérivée seconde
double F1_rond_F2::Der_sec(double x)
  {  ValDer2 ret1 = F2->Valeur_Et_der12(x);
     ValDer2 ret2 = F1->Valeur_Et_der12(ret1.valeur);
     return  ret1.der_sec * ret2.derivee + Sqr(ret1.derivee) * ret2.der_sec;
   };
    
    // 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
Courbe1D::Valbool  F1_rond_F2::Valeur_stricte(double x)  
  { Valbool ret1 = F2->Valeur_stricte(x);
    Valbool ret = F1->Valeur_stricte(ret1.valeur);
    // si un des deux n'est pas dedans le tout n'est pas dedans
    if ((!ret1.dedans) || (!ret.dedans)) ret.dedans = false;   
    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
Courbe1D::ValDerbool  F1_rond_F2::Valeur_Et_derivee_stricte(double x)  
  {  ValDerbool ret1 = F2->Valeur_Et_derivee_stricte(x);
     ValDerbool ret2 = F1->Valeur_Et_derivee_stricte(ret1.valeur);
     ValDerbool ret; ret.valeur=ret2.valeur; ret.derivee=ret1.derivee*ret2.derivee;
     if ((!ret1.dedans) || (!ret2.dedans)) 
      {ret.dedans = false;}
     else
      { ret.dedans = true;};        
     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 F1_rond_F2::Lecture_base_info(ifstream& 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 != "F1_ROND_F2")
         { cout << "\n erreur dans la verification du type de courbe lue ";    
           cout << "\n F1_rond_F2::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 F1_rond_F2::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; 
             };
          };    
        // pour la courbe2
        ent >> nom1 >> nom2 >> nom3;
        if (nom1 != "courbe2=")
         { cout << "\n erreur dans la verification du type, on attendait le mot cle courbe2= "
                << " et on a lu " << nom1 << " ";
           cout << "\n F1_rond_F2::Lecture_base_info(... ";
           Sortie(1); 
           }
        else
         { if (nom2 == "COURBE_INTERNE2")
            {// cas d'une courbe en interne
             // 1) on commence par effacer la courbe existante si nécessaire
             if (F2 != NULL) {if (F2->NomCourbe() == "_") delete F2;};
             // 2) on crée la courbe adoc
             nom2="_";
             F2 = Courbe1D::New_Courbe1D(nom2,Id_Nom_Courbe1D (nom3.c_str()));
             // 3) on lit les données particulières
             F2->Lecture_base_info(ent,cas);
             nom_courbe2="i_interne_i";
             }
           else
            {// cas d'une courbe externe on lit le nom
             nom_courbe1 = nom2;
             };
          };    
       }
   };

    // cas donne le niveau de sauvegarde
    // = 1 : on sauvegarde tout
    // = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void F1_rond_F2::Ecriture_base_info(ofstream& sort,const int cas)
  { // on n'a que des grandeurs constantes
    if (cas == 1)
      { sort << " F1_ROND_F2  ";
        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();;};
        if (F2->NomCourbe() == "_")
         {// cas d'une courbe interne
          sort << "\n courbe2=  COURBE_INTERNE2 " << F2->Type_courbe();
          F2->Ecriture_base_info(sort,cas);
          }
        else
          // cas d'une courbe externe
          {sort << "\n courbe2= " <<  F2->NomCourbe() << " " << F2->Type_courbe();};
       };
   };
         
// sortie du schemaXML: en fonction de enu 
void F1_rond_F2::SchemaXML_Courbes1D(ofstream& ,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;
		}
	};		
  };
     
