
// 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/>.

#ifndef  LesContacts_2_deja_inclus
  #include "LesContacts.h"
#endif

#ifndef  LesContacts_2_deja_inclus

#include <vector>
#include "ReferenceNE.h"
#include "ReferenceAF.h"


//----- lecture écriture base info -----
// lecture base info
// on utilise deux pointeurs de fonctions qui permettent de récupérer le pointeur de noeud esclave
// idem au niveau de l'élément 
template <class T>
void LesContacts::Lec_base_info_LesContacts(istream& ent
                        ,T& instance  // l'instance qui permet d'appeler les pointeurs de fonctions
                        ,Noeud& (T::*RecupNoeud)(int i, int j) const
                        ,Element& (T::*RecupElement_LesMaille)(int i, int j) const)
  {
   bool mono_proc_on_lit_un_BI_multiProc = false;
   int taille=0; // taille par défaut
   int niveau_commentaire_lescontacts = Permet_affichage();

#ifdef UTILISATION_MPI
   int proc_en_cours = ParaGlob::Monde()->rank();
   // === première partie commune à tous les proc
   // normalement tous les buffers ont été rempli, on se contente de rediriger les flux
   {// on crée le flux sur le premier buffer:
    istrstream * entrons = new istrstream(buffer_ioBI_MPI(1).c_str(),buffer_ioBI_MPI(1).size()) ;
    istream& ent = *entrons; // on redéfinit entr
#endif
    if (Permet_affichage()>4)
      cout << "\n -- LesContacts::Lec_base_info_LesContacts ";
      
    // tout d'abord on lit le type == l'entête
    string type_contact,no_taille;
    ent >> type_contact ; // entête
    // dans le cas où on lit un fichier créé avec MPI io, et que l'on n'est pas
    // en MPI, on passe les offsets et tailles
  #ifndef UTILISATION_MPI
    if (type_contact == "buf0_LesContacts")
     {for (int i=0;i<3;i++) //
       {type_contact.clear();
        std::getline(ent, type_contact);
       };
      mono_proc_on_lit_un_BI_multiProc = true;
     };
  #endif

    if (mono_proc_on_lit_un_BI_multiProc) // cas de la lecture en mono
     {ent >> type_contact >> no_taille >> taille;}   // d'un fichier créé en multi proc
    else // cas d'une lecture en mono d'un fichier mono
         // ou cas d'une lecture multi d'un fichier créé en multi proc
     {ent >> no_taille >> taille;}; //  on a déjà lue type_contact


//    // tout d'abord on lit le type
//    ent >> type_contact >> no_taille >> taille;
  #ifdef MISE_AU_POINT
    if (type_contact != "LesContacts:")
      { cout << "\n erreur dans la lecture des contacts, on attendait la chaine: LesContacts:"
             << " alors que l'on a lue : " << type_contact
             << "\n LesContacts::Lec_base_info_LesContacts(i...";
        Sortie(1);     
       };
    if (no_taille != "taille")
      { cout << "\n erreur dans la lecture des contacts, on attendait la chaine: taille"
             << " alors que l'on a lue : " << no_taille
             << "\n LesContacts::Lec_base_info_LesContacts(i...";
        Sortie(1);     
       };
  #endif
  
#ifdef UTILISATION_MPI
   }; // fin du premier buffer
#endif
    // on supprime tous les éléments de contact sauf... les collants qui restent collant
    {LaLIST <ElContact>::iterator iE ;
     // on met .end(), car cette borne peut changer au gré des suppression
     for (iE = listContact.begin(); iE != listContact.end(); iE++)
       if (!((*iE).Collant())   // et si ce n'est pas collant: si collant, on ne change rien
          )
         { // sinon on supprime
           LaLIST <ElContact>::iterator iiE = iE;
           iE--; // pointe sur le precedent element
           LaLIST < LaLIST<ElContact>::iterator >  & list_tesN
                                 = tesN_encontact((*iiE).Esclave()->Num_Mail())[(*iiE).Esclave()];
           list_tesN.remove(iiE);
           listContact.erase(iiE); // efface l'element
         };
    };
       Tableau <int> compteur_inesc(nb_mail_Esclave,1); // compteur de compteur de noeuds esclaves
   
    // on initialise la liste des éléments en contact
//    listContact.clear(); // comme on redémarre, on efface la situation actuelle
    // non, il faut garder les éléments collants
    
    
    // --- on s'occupe du deuxième niveau de tesN_encontact
    // une map vide par défaut
// *** non car cela supprime tous les noeuds d'éléments collants
//    std::map<Noeud*,LaLIST < LaLIST<ElContact>::iterator > > map_vide;
//    tesN_encontact.Change_taille(nb_mail_Esclave,map_vide);
    
#ifdef UTILISATION_MPI
     // === deuxième partie commune à tous les proc i != 0
     // par contre seules les éléments de contact qui sont relié aux éléments finis
     // gérés par le proc i, sont pris en compte par proc i
     if ( proc_en_cours != 0)
      {// on crée le flux sur le premier buffer:
       istrstream * entrons = new istrstream(buffer_ioBI_MPI(2).c_str(),buffer_ioBI_MPI(2).size()) ;
       istream& ent = *entrons; // on redéfinit entr
#endif

       for (int i=1;i<= taille;i++)
           {// on commence par créer un élément de contact de base en fonction des infos sauvegardées
            // --- le noeud esclave
            string toto; int numMail_esclave, numNoeud_esclave;
            string type;
            int numMail_element, num_element, num_front_element;
            Enum_type_geom enu ; string nom_enu; //N_es 1 72 El 2 2 SURFACE  1
            ent >> toto >> numMail_esclave >> numNoeud_esclave  // le noeud esclave
                >> type >>  numMail_element >> num_element   // partie relative à l'élément
                >> enu  >> num_front_element; // et la frontière
            // on récupère les infos relatives à la frontière initiale
            int numMail_element_0, num_element_0, num_front_element_0;
            string type_0;Enum_type_geom enu_0 ;
            ent >> type_0 >>  numMail_element_0 >> num_element_0   // partie relative à l'élément
                >> enu_0  >> num_front_element_0; // et la frontière
             
            // on ne continue que si le noeud fait partie des maillages esclave ou en auto-contact
            // le nombre de maillages esclave et en auto-contact, peut-être redéfinit lors d'un restart
            // donc n'est pas sauvegardé
            if (numMail_esclave <= nb_mail_Esclave)
             {    Noeud& no = (instance.*RecupNoeud)(numMail_esclave, numNoeud_esclave); // ""
               // --- création de la frontière
               Element& elem = (instance.*RecupElement_LesMaille)(numMail_element,num_element);
               ElFrontiere*  elfr;
               switch (enu)
               {  case POINT_G : elfr = elem.Frontiere_points(num_front_element,true) ; break;
                  case LIGNE : elfr = elem.Frontiere_lineique(num_front_element,true) ; break;
                  case SURFACE : elfr = elem.Frontiere_surfacique(num_front_element,true) ; break;
                  default :
                    cout << "\nErreur : valeur incorrecte du type de frontiere " << Nom_type_geom(enu) << " !\n";
                    cout << "LesContacts::Lec_base_info_LesContacts(... \n";
                    Sortie(1);
               };
               // création d'un Front
               Front froont(*elfr,&elem,num_front_element);
               // froont ne contient pas forcément les éléments mitoyens, en particulier pour les élément d'angles morts
               // par contre il doit correspondre à un des éléments enregistrés dans t_listFront
               // on va donc le chercher dans t_listFront
               Front* froant=NULL; // le front que l'on cherche
               
               // ** pour le tableaux t_listFront, le numéros dit de maillage, n'est pas le numéro
               // ** intrinsèque de maillage (telle que ceux associés aux noeuds et éléments)
               // ** mais uniquement un numéro locale d'ordre
               // ** mais on a: les éléments de frontière de t_listFront(i) font partie du maillage
               // i + (nb_mail_Esclave-nbmailautocontact)
               int ii = numMail_element - (nb_mail_Esclave-nbmailautocontact);
               Tableau < LaLIST_io <Front> >& t_listFront_i = t_listFront(ii); // pour simplifier
               int nbzoneEt1 = 1+t_listFront_i.Taille();
               int num_zone = 0; // le numéro de zone éventuel
               bool trouver = false;
               for (int izone = 1; izone < nbzoneEt1;izone++)
                {LaLIST_io <Front>& t_listFront_i_izone = t_listFront_i(izone); // pour simplifier
                 LaLIST_io <Front>::iterator it,itfin=t_listFront_i_izone.end();
                 for (it = t_listFront_i_izone.begin();it != itfin; it++)
                  {if ((*it).MemeOrigine(froont))
                    {froant = &(*it);
                     trouver = true;
                     break;
                    };
                  };
                 if (trouver)
                  {num_zone=izone; break;}
                };
              // on vérifie que l'on a bien trouvé un front sinon, on signale
              if ((trouver == false) && (ParaGlob::NiveauImpression() > 0))
               {cout << "\n *** attention la frontiere de contact ";
                froont.Affiche(1);
                cout << " n'existe plus, on n'en tient plus compte ";
               };
              // de manière analogue on s'occupe de la frontière initiale
              Front* froant_0=NULL; // le front que l'on cherche
               Element& elem_0 = (instance.*RecupElement_LesMaille)(numMail_element_0,num_element_0);
               ElFrontiere*  elfr_0;
               switch (enu_0)
               {  case POINT_G : elfr_0 = elem.Frontiere_points(num_front_element_0,true) ; break;
                  case LIGNE : elfr_0 = elem.Frontiere_lineique(num_front_element_0,true) ; break;
                  case SURFACE : elfr_0 = elem.Frontiere_surfacique(num_front_element_0,true) ; break;
                  default :
                    cout << "\nErreur : valeur incorrecte du type de frontiere initiale " << Nom_type_geom(enu_0) << " !\n";
                    cout << "LesContacts::Lec_base_info_LesContacts(... \n";
                    Sortie(1);
               };
               // création d'un Front
               Front froont_0(*elfr_0,&elem_0,num_front_element_0);
               // froont_0 ne contient pas forcément les éléments mitoyens, en particulier pour les élément d'angles morts
               // par contre il doit correspondre à un des éléments enregistrés dans t_listFront
               // on va donc le chercher dans t_listFront
               
               // ** pour le tableaux t_listFront, le numéros dit de maillage, n'est pas le numéro
               // ** intrinsèque de maillage (telle que ceux associés aux noeuds et éléments)
               // ** mais uniquement un numéro locale d'ordre
               // ** mais on a: les éléments de frontière de t_listFront(i) font partie du maillage
               // i + (nb_mail_Esclave-nbmailautocontact)
               bool trouver_0 = false;
               for (int izone = 1; izone < nbzoneEt1;izone++)
                {LaLIST_io <Front>& t_listFront_i_izone = t_listFront_i(izone); // pour simplifier
                 LaLIST_io <Front>::iterator it,itfin=t_listFront_i_izone.end();
                 for (it = t_listFront_i_izone.begin();it != itfin; it++)
                  {if ((*it).MemeOrigine(froont_0))
                    {froant_0 = &(*it);
                     trouver_0 = true;
                     break;
                    };
                  };
                 if (trouver_0)
                  {break;}
                };
              // on vérifie que l'on a bien trouvé un front sinon, on signale
              if ((trouver_0 == false) && (ParaGlob::NiveauImpression() > 0))
               {cout << "\n *** attention la frontiere initiale de contact ";
                froont_0.Affiche(1);
                cout << " n'existe plus, on n'en tient plus compte "
                     << " mais cela va induire des erreurs si l'element participe "
                     << " a du frottement ou a un contact collant " ;
               };
               
              // même si l'élément n'existe plus
              // on continue la création pour ne pas corrompre la lecture
              // on créer un élément de contact ad hoc
              // création de l'élément de contact
              ElContact courant(&froont,&no,fct_nD_contact,&froont_0);
              // lecture des données particulières (qui ne contiennent pas les mitoyens)
              courant.Lec_base_info_ElContact(ent);
          #ifdef UTILISATION_MPI
              // dans le cas // on ne continue que si l'élément de contact est relatif à un élément associé au cpu
              if ((ParaGlob::param->Element_concerner(numMail_element,num_element )))
          #endif
               {// on s'occupe d'abord de la frontière initiale
                if (trouver_0)
                  {// on met à jour les mitoyens pour froont_0
                   // definition des elements mitoyens à partir du front enregistré qui lui contient les mitoyens
                   // qui ont été créé à l'initialisation
                   if (froant_0->TabMitoyen() != NULL)
                     courant.Elfront_initiale()->DefMitoyen(*(froant_0->TabMitoyen()));
                 #ifdef UTILISATION_MPI
                   // on attribue au front de l'élément de contact, le numéro unique utilisable pour le tableau pointe_t_listFront
                   courant.Elfront_initiale()->ChangeNumUnique(froant_0->NumUnique());
                 #endif
                 };
                // puis la frontière courante
                if (trouver)
                  {// on met à jour les mitoyens
                   // definition des elements mitoyens à partir du front enregistré qui lui contient les mitoyens
                   // qui ont été créé à l'initialisation
                   if (froant->TabMitoyen() != NULL)
                     courant.Elfront()->DefMitoyen(*(froant->TabMitoyen()));
                   // mis à jour du numéro de zone
                   courant.Num_zone_contact() = num_zone;
                   courant.Change_lissage_normale(lissage_de_la_normale(num_zone)); // affectation du lissage
                 #ifdef UTILISATION_MPI
                   // on attribue au front de l'élément de contact, le numéro unique utilisable pour le tableau pointe_t_listFront
                   courant.Elfront()->ChangeNumUnique(froant->NumUnique());
                 #endif
                   // sauvegarde si l'élément de contact n'existe pas déjà
                   ElContact* test_existance= Element_contact_deja_present(courant);
                   if (test_existance == NULL)
                    // on enregistre
                      {listContact.push_front(courant);
                       LaLIST<ElContact>::iterator ipp = listContact.begin();
                     // on met à jour le tableau tesN_encontact
                //*** la liste peut très bien ne pas exister !
            //         #ifdef MISE_AU_POINT
            //           if (tesN_encontact(numMail_esclave).find((*ipp).Esclave())
            //                 == tesN_encontact(numMail_esclave).end() )
            //            { cout << "\n*** Erreur : on ne trouve pas la liste d'element en contact avec le noeud esclave "
            //                   << (*ipp).Esclave()->Num_noeud() << " du maillage " << numMail_esclave
            //                   << " la suite n'est pas possible "
            //                   << " LesContacts::Lec_base_info_LesContacts(.. \n";
            //              Sortie(1);
            //            };
            //         #endif
                    
                       // ajout dans la liste associé au noeud esclave
                       tesN_encontact(numMail_esclave)[(*ipp).Esclave()].push_front(listContact.begin());
                      };
                 };
               };
             }    // fin de l'ajout d'un élément de contact
            else
             {if (ParaGlob::NiveauImpression() > 0)
                cout << "\n *** attention, le maillage "<<numMail_esclave<<" n'est plus esclave !"
                     << " l'element de contact non pris en compte: "
                     << "\n   N_es " <<numMail_esclave << " "<<numNoeud_esclave
                     << type << " "<< numMail_element << " "<< num_element << " "
                     << Nom_type_geom(enu) <<" "<<num_front_element;
             };
            }; // fin de la boucle for (int i=1;i<= taille;i++)
#ifdef UTILISATION_MPI
       }; // fin du deuxième buffer
#endif

    if (Permet_affichage()>4)
      // on va lister les éléments de contact
     { cout << "\n liste des Elcontact  (fct du niveau de commentaire des elcontact): ";
       LaLIST<ElContact>::iterator ipp,ippfin=listContact.end();
       for (ipp=listContact.begin();ipp != ippfin; ipp++)
         {(*ipp).Affiche(2);};
     };
    Calcul_Nb_contact_actif();
    if (((ParaGlob::NiveauImpression() > 4) && (nb_contact_actif != 0))
        || (niveau_commentaire_lescontacts > 4 )
       )
  #ifndef UTILISATION_MPI
        cout << "\n >> restart: lecture elements contact : "<< nb_contact_actif << " elements lus " << endl ;
  #else
        cout << "\n >> restart: proc "<< proc_en_cours <<", lecture elements contact : "<< nb_contact_actif << " elements lus " << endl ;
  #endif



#ifdef UTILISATION_MPI
  if ( proc_en_cours == 0)
   // === dernière partie spécifique à proc 0
   {// on crée le flux sur le premier buffer:
    istrstream * entrons = new istrstream(buffer_ioBI_MPI(3).c_str(),buffer_ioBI_MPI(3).size()) ;
     istream& ent = *entrons; // on redéfinit entr
#endif
     string mot;
     ent >> mot >> tempsContact;
#ifdef UTILISATION_MPI
   }; // fin du premier buffer
#endif
     
  };
     
 #endif   // fin du test d'inclusion : ifndef  LesContacts_2_deja_inclus



