
// 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 "Front.h"
#include "ParaAlgoControle.h"

      // constructeur
          //par defaut
Front::Front() :
    boite_Front(),num_frontiere(0),elem(NULL),angle_mort(0)
#ifdef UTILISATION_MPI
    ,num_unique(0)
#endif
        {ptEl = NULL; tabmitoyen=NULL;  };
          // normal  
Front::Front ( const ElFrontiere& el,  Element * pt, int num_front ,int ang_mort) :
    boite_Front(ParaGlob::Dimension()),num_frontiere(num_front)
    ,angle_mort(ang_mort)
#ifdef UTILISATION_MPI
    ,num_unique(0)
#endif
	  ,elem(el.NevezElemFront())
       {  ptEl =(Element *) pt;
          tabmitoyen=NULL;
          // construction de la boite d'encombrement pour l'element
          Enum_dure temps=TEMPS_0;  // temps par défaut
          if ((ptEl->Tab_noeud())(1)->ExisteCoord1()) temps = TEMPS_t;
          // construction de la boite d'encombrement pour la frontière
          Boite_encombrement_frontiere(temps,0.);
       };
          // de copie
Front::Front ( const Front& a) :
      boite_Front(a.boite_Front),num_frontiere(a.num_frontiere)
		,elem(a.elem->NevezElemFront()),angle_mort(a.angle_mort)
#ifdef UTILISATION_MPI
    ,num_unique(a.num_unique)
#endif
        { ptEl = (Element *) a.ptEl;
          if (a.tabmitoyen != NULL)
            { int tabtaille = (a.tabmitoyen)->Taille();
              if (tabtaille != 0)
                { tabmitoyen= new Tableau <Front*>;
                  *tabmitoyen = *(a.tabmitoyen);
                }
               else
                 tabmitoyen = NULL;
              }
           else 
             tabmitoyen = NULL;   
        };    
        // destructeur
Front::~Front() 
     { if (elem != NULL) 
		     delete elem;
       // on efface le tableau mais pas les objets pointés       
       if (tabmitoyen != NULL)
           delete tabmitoyen;
      };   
 // =============     METHODES    ==========        
          
// affichage à l'écran des informations liées au contact
// cas = 0 : affichage en fonction du niveau de commentaire voulu
// cas = 1 : affichage minimal nécessaire pour repérer l'élément
void Front::Affiche(int cas) const
{ if (elem != NULL)
   {if (cas == 1)
    { cout << " front " ;
      if (angle_mort)
        cout << "(angle mort) ";
      cout << num_frontiere << " de l'EF " << ptEl->Num_elt() << " du mail. "
           << ptEl->Num_maillage() <<   flush;
    }
    else
     {cout << "\n element de frontiere du maillage " << ptEl->Num_maillage() <<" , cree par l'element "
           << ptEl->Num_elt() << " de numero " << num_frontiere << " , de type de geometrie: "
           << Nom_type_geom(elem->Type_geom_front()) << " , avec une interpolation:"
           << Nom_interpol(elem->ElementGeometrique().TypeInterpolation());
      if (angle_mort) cout << " ( represente un angle mort) , ";
      cout << " , constitue des noeuds: " ;
		    const Tableau <Noeud *>& tabnoeud = elem->TabNoeud_const();
		    int tabtaille = tabnoeud.Taille();
		    for (int i=1;i<=tabtaille;i++)
		   	  cout << tabnoeud(i)->Num_noeud() << " , ";
		    cout << " , et contiend les degres de liberte suivants: "
		   	    << elem->DdlElem_const();
		    if (this->tabmitoyen != NULL)
		   	{int tai = this->tabmitoyen->Taille();
		   	 cout << "\n " << tai  << " elements mitoyens: ( ";
		   	 for (int i=1;i<=tai;i++)
		   	    cout << (*tabmitoyen)(i)->PtEI()->Num_elt() << " , ";
		   	 cout << ")  ";
		   	}
		    else
		   	 cout << "\n *** aucun element mitoyen !!!! ***  ";
     };
	  }
	else 
	 { cout << "\n element de frontiere associe a un element fini: FRONTIERE NON DEFINIE !! "; };
		
//   cout << "\n ";
  };      
      
// affectation de toute les donnees
Front& Front::operator = ( const Front& a)
{ if (elem != NULL)
   { if (a.elem == NULL) {delete elem; elem = NULL;}
		   else {*elem = *a.elem;};
	  }
	 else
   { if (a.elem != NULL) 
		    {elem = a.elem->NevezElemFront();};
		   //sinon les deux sont nuls on n'à rien à faire
   };
  num_frontiere = a.num_frontiere;
  ptEl = (Element *)  a.ptEl;
  if (a.tabmitoyen != NULL)
     { if (tabmitoyen == NULL)
         tabmitoyen= new Tableau <Front*>;
       *tabmitoyen = *(a.tabmitoyen);
      }
  else
     {if (tabmitoyen != NULL)
          {delete tabmitoyen;
           tabmitoyen = NULL; 
          };
		   };
	 // boite d'encombrement 
  boite_Front = a.boite_Front;
  // angle mort
  angle_mort = a.angle_mort;
#ifdef UTILISATION_MPI
  num_unique = a.num_unique; 
#endif
  return *this;
};

// definition des elements mitoyens
void Front::DefMitoyen(const Tableau <Front*>& tab)
  { 
    int tabtaille = tab.Taille();
    if (tabtaille != 0)
     {if (tabmitoyen == NULL)
         tabmitoyen= new Tableau <Front*>;
      *tabmitoyen = tab;
      }
    else 
     {if (tabmitoyen != NULL)
          {delete tabmitoyen;
           tabmitoyen = NULL; 
          };
	    };
  };
// ajout d'un élément mitoyens (l'ajout est effectif uniquement s'il n'existe pas déjà)
void Front::AjoutMitoyen(Front * mitoyen)
 { if (tabmitoyen == NULL)
     // si le tableau n'existe pas, on le dimensionne à 1 et on ajoute l'unique élément
     {tabmitoyen= new Tableau <Front*>;
      tabmitoyen->Change_taille(1);
      (*tabmitoyen)(1) = mitoyen;
     }
   else // sinon on commence par regarder si mitoyen existe déjà
    {int tail = tabmitoyen->Taille();
     bool trouver = false;
     for (int i=1;i<= tail; i++ )
       if (*(*tabmitoyen)(i) == *mitoyen)
        {trouver = true; break;}
     // s'il n'existe pas on l'ajoute
     if (!trouver)
      // on augmente la taille et on ajoute
      {tabmitoyen->Change_taille(tail+1);
       (*tabmitoyen)(tail+1)= mitoyen;
      };
    };
 };
 
 // on retire un mitoyen
 // retourne true si la suppression est effective, false si la suppression est erronée
 bool Front::SuppressionMitoyen(Front * mitoyen)
 { bool trouver = false;
   if (tabmitoyen != NULL)
     // si le tableau n'existe pas, on ne peut rien supprimer
     // sinon on regarde si mitoyen existe 
    {int tail = tabmitoyen->Taille();
     int i;
     for (i=1;i<= tail; i++ )
       if (*(*tabmitoyen)(i) == *mitoyen)
        {trouver = true; break;}
     if (trouver)
       {// s'il existe il faut redimentionner le tableau
        Tableau <Front*> tabmitoyen_inter(*tabmitoyen); // tableau inter
        tabmitoyen->Change_taille(tail-1);
        for (int j=i;j< tail; j++ )
          (*tabmitoyen)(j) = tabmitoyen_inter(j+1);
       };
    };
   // retour
   return trouver;
 };


// test si le point passé en argument appartient à la boite d'encombrement de la frontière
// tous les points sont supposées avoir la même dimension
bool Front::In_boite_emcombrement_front(const Coordonnee& M) const
{ // on va essayer de faire efficace, pour cela on test le minimum
  const Coordonnee& co_min = boite_Front.Premier();  // par commodité
  const Coordonnee& co_max = boite_Front.Second();   // par commodité
  #ifdef MISE_AU_POINT
  if ((M.Dimension() != ParaGlob::Dimension())|| (M.Dimension()	!= co_min.Dimension()))
  	{ cout << "\n *** pb de dimensions non coherente !! "
  	       << "\n Front::In_boite_emcombrement_front(...";
  	  Sortie(1);     
  	};
  #endif
  switch (ParaGlob::Dimension())
   { case 3: if ((M(3)<co_min(3)) || (M(3) > co_max(3))) return false;
   	 case 2: if ((M(2)<co_min(2)) || (M(2) > co_max(2))) return false;
   	 case 1: if ((M(1)<co_min(1)) || (M(1) > co_max(1))) return false;
   };
  // si on arrive ici c'est que le point est interne
  return true; 
}; 

// test si le point passé en argument appartient à la boite d'encombrement de la frontière
// tous les points sont supposées avoir la même dimension
// ici la boite est augmentée de extra dans tous les sens
bool Front::In_boite_emcombrement_front(const Coordonnee& M,double extra) const
{ // on va essayer de faire efficace, pour cela on test le minimum
  const Coordonnee& co_min = boite_Front.Premier();  // par commodité
  const Coordonnee& co_max = boite_Front.Second();   // par commodité
  #ifdef MISE_AU_POINT
  if ((M.Dimension() != ParaGlob::Dimension())|| (M.Dimension() != co_min.Dimension()))
   { cout << "\n *** pb de dimensions non coherente !! "
          << "\n Front::In_boite_emcombrement_front(...";
     Sortie(1);
   };
  #endif
  switch (ParaGlob::Dimension())
   { case 3: if ((M(3)<(co_min(3)-extra)) || (M(3) > (co_max(3)+extra))) return false;
     case 2: if ((M(2)<(co_min(2)-extra)) || (M(2) > (co_max(2)+extra))) return false;
     case 1: if ((M(1)<(co_min(1)-extra)) || (M(1) > (co_max(1)+extra))) return false;
   };
  // si on arrive ici c'est que le point est interne à l'extra près
  return true;
};
 
//----- lecture écriture base info -----
// lecture base info
// ici la lecture n'est pas complète il faut ensuite changer l'élément
// frontière et changer le pointeur de l'élément finis
// -> utilisation de : Change_elem_frontiere, Change_PtEI
void Front::Lecture_base_info_front(istream& ent)
 { // lecture et vérification du type
   string nom_type;
   ent >> nom_type;
   if (nom_type != "Front")
     Sortie(1);
   // lecture des infos
	  string toto;
	
	  // les éléments spécifiques à l'élément frontière
	  int test = 0;
	  ent >> test;
	  if (test)
	    {elem->Lecture_base_info_ElFrontiere_pour_projection(ent);}
   // on réutilise nom_type pour lire la chaine: am=
   ent >> nom_type >> angle_mort;

//   // la boite d'encombrement  de la frontière
//   ent >> nom_type >> boite_Front.Premier() >>  boite_Front.Second() ;        
  };
//   écriture base info
// on ne sauvegarde ici que les grandeurs spécifiques à ce niveau
// concernant les infos relatives à l'élément, la frontière, etc, ce n'est pas traité ici, mais au niveau de la classe LesContats   
void Front::Ecriture_base_info_front(ostream& sort)
  { // écriture du type
    sort << "Front ";
    // les données
//    sort << "mail= " << ptEl->Num_maillage() << " NE= " << ptEl->Num_elt() << " fron= " << num_frontiere ;
	 // les éléments spécifiques à l'élément frontière
	   if (elem != NULL)
	     { sort << " 1 "; elem->Ecriture_base_info_ElFrontiere_pour_projection(sort);}
	   else { sort << " 0 ";};
    sort << " am= " << angle_mort << " ";
    
    // la boite d'encombrement 
//    sort << "encombrements_surf " << boite_Front.Premier() << " " << boite_Front.Second() << " \n";
  };

//====================================== fonctions internes ============================
    // fonctions internes

    // mise à jour de la boite d'encombrement de la frontière
    // dep_max : déplacement maxi des noeuds du maillage
    //           , sert pour def des boites d'encombrement maxi des frontières
void Front::Boite_encombrement_frontiere(Enum_dure temps,double dep_max)
{ 
	#ifdef MISE_AU_POINT
	 if (elem == NULL)
	    {cout << "\n *** erreur, la frontiere n'est pas definie  "
				       << "\n Front::Boite_encombrement_frontiere(... " << endl;
		    Sortie(1);	
		   };	 
	#endif
  
  Tableau <Noeud *>& tab = elem->TabNoeud();
  int tab_taille= tab.Taille();

  Coordonnee& encomb_min_fr=boite_Front.Premier();
  Coordonnee& encomb_max_fr=boite_Front.Second();
  switch (temps)
   { case TEMPS_0:
      {encomb_min_fr=tab(1)->Coord0();encomb_max_fr=encomb_min_fr; // init
       for (int i=2;i<=tab_taille;i++) // balayage des autres noeuds
          { Coordonnee co=tab(i)->Coord0();
            encomb_min_fr.Modif_en_min(co); encomb_max_fr.Modif_en_max(co);
          };
       break;		
    	 }
     case TEMPS_t:
      {encomb_min_fr=tab(1)->Coord1();encomb_max_fr=encomb_min_fr; // init
       for (int i=2;i<=tab_taille;i++) // balayage des autres noeuds
          { Coordonnee co=tab(i)->Coord1();
            encomb_min_fr.Modif_en_min(co); encomb_max_fr.Modif_en_max(co);
          };
       break;		
    	 }
     case TEMPS_tdt:
      {encomb_min_fr=tab(1)->Coord2();encomb_max_fr=encomb_min_fr; // init
       for (int i=2;i<=tab_taille;i++) // balayage des autres noeuds
          { Coordonnee co=tab(i)->Coord2();
            encomb_min_fr.Modif_en_min(co); encomb_max_fr.Modif_en_max(co);
          };
       break;		
    	 }
   }; // fin du switch
  // maintenant on tiend compte d'un facteur majorant pour incertitude
  Coordonnee delta=(encomb_max_fr- encomb_min_fr) 
                      * (ParaGlob::param->ParaAlgoControleActifs().Extra_boite_prelocalisation()-1.);
  // dans le cas de surface plane on peut avoir une boite avec une épaisseur nulle,
  // comme il s'agit d'une boite d'encombrement assez grossière on ajoute une valeur par défaut
  // par défaut on prend une valeur par défaut du max de delta
  double miniajout = (ParaGlob::param->ParaAlgoControleActifs().Rapport_Extra_boite_mini_prelocalisation())
                     * delta.Max_val_abs();
	 // dans le cas d'un espace 3D et d'une frontière provenant d'un élément 2D avec une épaisseur, 
	 // on prend le maxi de l'épaisseur et du miniajout déjà calculé
	 if ((ParaGlob::Dimension() == 3) && (Type_geom_generique(ptEl->ElementGeometrique().TypeGeometrie()) == SURFACE))
//	          ((ptEl->PoutrePlaqueCoque() == PLAQUE) || (ptEl->PoutrePlaqueCoque() == COQUE))
//		  )
	   { // on récupère l'épaisseur
		    ElemMeca* elemmeca = (ElemMeca *) ptEl ;
      miniajout = MaX(miniajout, elemmeca->EpaisseurMoyenne(temps));
		  }; 	
							  
  delta.Ajout_meme_valeur(miniajout);
  // ajout d'un extra dans toutes les directions
  delta.Ajout_meme_valeur(ParaGlob::param->ParaAlgoControleActifs().Ajout_extra_boite_prelocalisation());

  encomb_min_fr -= delta; encomb_max_fr += delta;
  // on tiend enfin compte d'une majoration de 2 fois le déplacement maxi des noeuds du maillage
  if (dep_max != 0.)
     {encomb_min_fr.Ajout_meme_valeur(-2.*dep_max);
      encomb_max_fr.Ajout_meme_valeur(2.*dep_max);
     };
// -- debug 
//if( ptEl->Num_elt() == 9)
//  { cout << "\n elem " << ptEl->Num_elt() << " maillage " << ptEl->Num_maillage() << " frontiere  N= " ;
//	for (int i=1;i<= (elem->TabNoeud()).Taille();i++) cout << (elem->TabNoeud())(i)->Num_noeud() << " ";
//	cout << "\n boite ";  encomb_min_fr.Affiche(); encomb_max_fr.Affiche(); 
//	cout << " Front::Boite_encombrement_frontiere( " << endl;
//	};
//-- fin débug	
	  
};
