/*
 * ADAPT2D : a software for automatic mesh adaptation in 2D
 *
 * AUTHOR : Manuel J. Castro Diaz(e-mail:castro@gamba.cie.uma.es)
 * ADAPTED FOR FREEFEM : Prud'homme Christophe (e-mail:prudhomm@ann.jussieu.fr) 
 *
 * this code is public domain
 * 
 * You may copy freely these files and use it for    
 * teaching or research. These or part of these may   
 * not be sold or used for a commercial purpose without
 * our consent
 * 
 * Any problems should be reported to the AUTHOR
 * at the following address : castro@gamba.cie.uma.es
 */


/* ------------------------------------------------------------------
 
                     SUBRRUTINA REGENERA_MALLA


         Proposito:   Esta subrrutina reedita un nuevo mallado
         ---------    a partir del mallado malla_metr, donde tenemos
                      definida una metrica. Para ello hacemos una
                      copia del mallado malla_metr y la vamos modifi
                      cando segun un criterio. Estas modificaciones
                      consisten en an~adir un punto, suprimirlo o
                      moverlo.

         Entrada:     
         -------               
                      malla_metr= Mallado que quermos modificar y
                                  en el que hemos definido una
                                  metrica.
                      
         Salida:      Esperemos que un mallado o algo que se parezca.
         ------
*/

#include <m_reg.hxx>


Triangulation* regenera_malla(Mallado_T0* malla, Scalar* solucion,char*
                              timesh, int sol,int nsol, char* sol_interp,int
                              angulo,int nit, Boolean segur,double*
                              tiempo,Boolean reg_inicial,Boolean reg_fin,
                              Boolean rel_mtr,Boolean fluid,Scalar hwall,int
                              refwall,CAD* cad,int tinterp,Scalar**sol_int
#ifndef ADAPT2D
                              ,Scalar *func, int nfunc, Scalar **interp_func
#endif
)
{
  Mallado_T1* m1;
  Triangulation* t_fin;
  p_lkArista La_loc[3];   
  FraristaCAD_link* cad_lk;
  Arista_CAD_dlist* dlist;
  Vertice_T1_dlink* Ls_lk;
  Vertice_T1* s0,*so,*s1,*su;
  Arista_T1_dlink* La_lk;
  Arista_T1* a0;
  Triangulo_T1_dlist* t_vec;
  Triangulo_T1_dlink* Lt_lk;
  Triangulo_T1* t0;
  p_Arete* la_lg,*la_op;
  R2 pt_pr;
  const Scalar infini=1e+36;
  Scalar d0,dist=infini;
  Scalar tiempo0,tiempo1,tiempo2,tiempo3;
  int nbs,nbt,num_t;
  int ca,minn,cnba,cnbaa;
  int j,err,err0;       
  int ss,aa; 
  int cont=0,p_loc=0,cont_t=0;
  int num=0;
  int num_iter,num_tot;
  int cifra,c_c,c_d,c_u;
  int prof=10,prof0=0;
  char ficnum[5];
  char cf[11]="0123456789";
  char ca1[72];
  char fic0[72];
  Boolean Suprimir=TRUE;
  Boolean Modificado=FALSE;
  Boolean terminar0;
  Boolean filt;
  Boolean fin;
  Boolean counter;
  
  m1=new Mallado_T1;
  if (m1==NIL) ERROR();
  /*
   *   Generating m1 from Mallado_T0 malla
   */
  m1->build(malla);
  /*
   *   Founding soubdomain intersection
   */
  busca_inters(m1->Lsommet);
  /*
   *  Mesh verification
   */
  
  verifica(m1->Ltriangle,m1->Larete,m1->Lsommet);

  /* 
   *   Initialization of CAD pointers
   */
  Ls_lk=m1->Lsommet->principio();
  while (Ls_lk) {
    s0=Ls_lk->s;
    if (s0->front==TRUE) {
      s0->a=proyeccion_cao(cad,s0->c,pt_pr,NIL,1,0);
    }
    else {
      if (s0->inters==TRUE){ 
        s0->a=proyeccion_cao(cad,s0->c,pt_pr,NIL,2,0);
      }
    }
    Ls_lk=Ls_lk->sig();
  }
  cad_lk=cad->principio();
  while (cad_lk) {
    dlist=cad_lk->Aretelist;
    dlist->lazos_lk();
    prof0=MAX(prof0,dlist->num_elem());
    cad_lk=cad_lk->sig();
  }
  if (prof>(prof0-1)/2) prof=MAX((prof0-1)/2,2);
  /*
   * Computing if necessary the number of iterations 
   * in metrix relaxation.
   */
  if (rel_mtr==TRUE) {
    num_iter=malla->calculo_factor();
    num_tot=num_iter;
    if (nit!=0) num_iter=MIN(nit,num_iter);
    if (nit<0) num_iter=-nit;
  }
  else {
    num_iter=1;
    if (nit<0) num_iter=-nit;
  }
#ifdef DEBUG
  cout<<"Number of iterations to be done: "<<num_iter<<endl;
  cout<<"--------------------------------"<<endl<<endl;
#endif /* DEBUG */
  /*
   *    Initialice triangle quality
   */
  filt=busca_criterio(m1->Ltriangle);
  /*
   *    Initial regularization if choosen
   */
  if (reg_inicial==TRUE) {
#ifdef DEBUG
    cout<<endl<<endl;
    cout<<"Initial Regularization:"<<endl;
    cout<<"-----------------------"<<endl<<endl;
    cout<<endl<<"Swapping.............."<<endl;
#endif /* DEBUG */
    La_lk=m1->Larete->principio();
    while (La_lk) {  
      a0=La_lk->a;
      if (a0->front==FALSE && a0->inters==FALSE) {
	t0=a0->tr;
	aa=t0->arista(a0);
	if (aa<0) {
	  cerr<<"Atention. Error in Sub. regenera_malla."<<endl;
	  cerr<<"Wrong pointers triangles-edges."<<endl;
	  exit(1);
	}
	else {
	  mshopt(t0,aa);
	}
      }
      La_lk=La_lk->sig();
    }   
  }
  the_clock(tiempo);
  tiempo1=*tiempo;
  /*
   *   Begining mesh adaptation loop
   */
#ifdef DEBUG
  cout<<endl<<endl;
  cout<<"-----------------------------------------"<<endl;
  cout<<"    BEGINING OF MESH ADAPTATION LOOP"<<endl;
  cout<<"-----------------------------------------"<<endl;
  cout<<endl;
#endif /* DEBUG */
 
  the_clock (tiempo);
  tiempo0=*tiempo;

  for (num=0; num<num_iter; num++) {
    if (rel_mtr==TRUE) terminar0=malla->cambio_factor(num,num_tot);
    cnba=m1->Larete->num_elem();
    La_lk=m1->Larete->principio();
    cont=0;
    counter=FALSE;
#ifdef DEBUG
    cout<<"Iteration N:"<<num+1<<endl;
    cout<<"------------"<<endl;
#endif /* DEBUG */
    fin=FALSE;
    ca=0;
    cnbaa=0;
    while (La_lk!=NIL && fin==FALSE) {
      Suprimir=TRUE;
      Modificado=FALSE;
      p_loc=0;
      cont++;
      dist=infini;
      su=NIL;
      a0=La_lk->a;
      so=a0->s[0];
      s1=a0->s[1];
      d0=distance(so->c,so->mtr,s1->c,s1->mtr);
      if (d0>1.4) {
	err=0;   
	while(d0>1.4 && err!=1) {
	  t0=a0->tr;             
	  err=mshap(m1->Ltriangle,m1->Larete,m1->Lsommet,t0,a0,malla,cad,prof);
	  if (err==0) {
	    if (counter) ca++;
	    if (a0->front==TRUE) 
	      cnba +=2;
	    else
	      cnba +=3;
	  }
	  if (err!=1) {
	    so=a0->s[0];
	    s1=a0->s[1];
	    d0=distance(so->c,so->mtr,s1->c,s1->mtr);
	  }
	}
	if (La_lk) La_lk=La_lk->sig();
      } 
      else {
	Suprimir=FALSE;
	if (d0<.6) {
	  Suprimir=TRUE;
	}             
	if (Suprimir==TRUE) {
	  for (j=0;j<3;j++) La_loc[j]=NIL;
	  su=s1;
	  t_vec=new Triangulo_T1_dlist;
	  if (t_vec==NIL) ERROR();
	  if (((so->front==TRUE || so->ref<0) && su->ref>=0) || (so->inters==TRUE)) {
	    t_vecinos(su,*t_vec);
	    err=mshsar(m1->Ltriangle,m1->Lsommet,La_loc,t_vec,su,so,a0,angulo,malla,cad,prof);
	    if (t_vec) {delete t_vec; t_vec=NIL;}
	    if (err==0) {
	      //cnbs--;
	      s0=so;
	    }
	  }
	  else {
	    t_vecinos(so,*t_vec);
	    err=mshsar(m1->Ltriangle,m1->Lsommet,La_loc,t_vec,so,su,a0,angulo,malla,cad,prof);
	    if (t_vec) {delete t_vec; t_vec=NIL;}
	    if (err==0) {
	      //cnbs--;
	      s0=su;
	    }
	  }
	  if (err==0) {
/*          
	    supresion de las aristas que sobran
	    */              
	    if (counter) ca++;
	    if (La_lk) La_lk=La_lk->sig();
	    for (j=0;j<3; j++) {
	      if (La_loc[j]!=NIL ) {
		if (La_lk!=NIL && La_lk==La_loc[j]) if (La_lk) La_lk=La_lk->sig();
		m1->Larete->kill(La_loc[j]);
		cnba--;
		La_loc[j]=NIL;
	      }
	    }
             
/*
  Etapa de optimizacion
  */
	    t_vec=new Triangulo_T1_dlist;
	    if (t_vec==NIL) ERROR();
	    t_vecinos(s0,*t_vec);
	    num_t=t_vec->num_elem();

            
	    la_lg=new p_Arete[num_t];
	    if (la_lg==NIL) ERROR();
	    la_op=new p_Arete[num_t];
	    if (la_op==NIL) ERROR();

	    Lt_lk=t_vec->principio();
	    cont_t=0;
	    while (Lt_lk) {
	      t0=Lt_lk->t;               
	      ss=t0->sommet(s0);
	      la_lg[cont_t]=t0->a[ss];
	      ss=t0->sommet_sig(s0);
	      la_op[cont_t]=t0->a[ss];
	      cont_t++;
	      Lt_lk=Lt_lk->sig();
	    }
	    for (j=0; j<num_t; j++) {                           
	      a0=la_lg[j];
	      so=a0->s[0];
	      s1=a0->s[1];
	      d0=distance(so->c,so->mtr,s1->c,s1->mtr);
	      if (d0>1.4) {
		err=0;   
		while(d0>1.4 && err!=1) { 
		  t0=a0->tr;         
		  err=mshap(m1->Ltriangle,m1->Larete,m1->Lsommet,t0,a0,malla,cad,prof);
		  if (err==0) {
		    if (counter) ca++;      
		    if (a0->front==TRUE)
		      cnba +=2;
		    else
		      cnba +=3;
		  }
		  if (err!=1) {
		    so=a0->s[0];
		    s1=a0->s[1];
		    d0=distance(so->c,so->mtr,s1->c,s1->mtr);
		  }
		  else {
		    t0=a0->tr;
		    ss=t0->arista(a0);
		    if (err==1) err0=mshopt(t0,ss);
		  }
		}
	      }
	      else {
		t0=a0->tr;
		ss=t0->arista(a0);
		err0=mshopt(t0,ss);
	      } 
	      t0=la_op[j]->tr;
	      ss=t0->arista(la_op[j]);
	      err0=mshopt(t0,ss);
	    }
	    if (la_op) {delete[] la_op; la_op=NIL;}
	    if (la_lg) {delete[] la_lg; la_lg=NIL;}
	  }
	  else {
	    if (La_lk) La_lk=La_lk->sig();
	  }
	  if (t_vec) {delete t_vec; t_vec=NIL;}
	}
	else {
	  if (La_lk) La_lk=La_lk->sig();
	} 
      }
      if ((cnba-cont)<50 && counter==FALSE) {
	counter=TRUE;
	cnbaa=cnba;
      }
      if (ca>30) {
	minn=MIN(5,int(cont/100));
	if (abs(cnba-cnbaa)<minn || cont>cnba*4) fin=TRUE;
#ifdef DEBUG
	cout<<"cnba:"<<cnba<<", cnbaa:"<<cnbaa<<endl;
	cout<<"cont:"<<cont<<endl;
#endif /* DEBUG */
	ca=0;
	cnbaa=cnba;
      }
    }
    if (nit<0) {
      filt=TRUE;
      filtrado (m1->Ltriangle,m1->Larete,m1->Lsommet,malla,angulo,4,filt,hwall,
                refwall,fluid,cad,prof);
    }
    Lt_lk=m1->Ltriangle->principio();
    while (Lt_lk) {
      Lt_lk->t->krit=-999;
      Lt_lk=Lt_lk->sig();
    }
    if (segur==TRUE) {
      cifra=num+1;
      if (cifra<10) {
	c_c=0;
	c_d=0;
	c_u=cifra;
      }
      else {
	if (cifra<100) {
	  c_c=0;
	  c_d=int(Scalar(cifra)/10.0);
	  c_u=cifra-c_d*10;
	}
	else {
	  c_c=int(Scalar(cifra)/100.0);
	  cifra=cifra-c_c*100;
	  c_d=int(Scalar(cifra)/10.0);
	  c_u=cifra-c_d*10;
	}
      }
      ficnum[2]=cf[c_u];
      ficnum[1]=cf[c_d];
      ficnum[0]=cf[c_c];
      ficnum[3]=char(0);
//      nbs=m1->Lsommet->num_elem();
//      nbt=m1->Ltriangle->num_elem();
      m1->Lsommet->numera();
      strcpy(ca1,sol_interp);
      strcat(ca1,ficnum);
      strcat(ca1,".bb");
      if (sol==1) { // interpolacion de la solucion en el nuevo mallado.
	*sol_int=interpolacion(m1->Lsommet,malla,nsol,solucion,m1->Ltriangle,tinterp);
        escsol(m1->Lsommet->num_elem(),nsol,ca1,*sol_int);
      }
#ifdef DEBUG      
      cout<<"Mesh saving. (Copy of security)."<<endl;
      cout<<"--------------------------------"<<endl<<endl;
#endif /* DEBUG */
      strcpy(fic0,timesh);
      strcat(fic0,ficnum);
      strcat(fic0,".am_fmt");
      err=m1->write(fic0);
    }
  }
  the_clock(tiempo);
  tiempo2=*tiempo;
#ifdef DEBUG
  cout<<endl;
  cout<<"-----------------------------------------"<<endl;
  cout<<"Mesh adaptation computation time :"<<(tiempo2-tiempo1)<<endl;
  nbt=m1->Ltriangle->num_elem(); 
  if (tiempo2-tiempo1>1e-5) {
    cout<<"Number of elements by minute:"<<int(Scalar(nbt)*60.0/(tiempo2-tiempo1))<<endl;
  } 
  cout<<"Total number of elements:"<<nbt<<endl;
  cout<<"------------------------"<<endl<<endl;
#endif /* DEBUG */
  if (reg_fin==TRUE) {
    filt=TRUE;
    filtrado (m1->Ltriangle,m1->Larete,m1->Lsommet,malla,angulo,4,filt,hwall,refwall,fluid,cad,prof);
  }
  the_clock(tiempo);
  tiempo3=*tiempo;
#ifdef DEBUG
  cout<<endl<<"Mesh regularization computational time:"<<(tiempo3-tiempo2);
  cout<<endl<<"--------------------------------------"<<endl<<endl;  
  cout<<"***********************************************************"<<endl;
  cout<<"           CHARACTERISTIC OF ADAPTED MESH"<<endl;
  cout<<"***********************************************************"<<endl;
#endif /* DEBUG */
  nbs=m1->Lsommet->num_elem();
  m1->Lsommet->numera();
  cal_long(m1->Larete,malla,refwall,nbs);
  verifica(m1->Ltriangle,m1->Larete,m1->Lsommet);    
  filt=busca_criterio(m1->Ltriangle);
   


#ifdef ADAPT2D
if (sol==1) { 
  *sol_int=interpolacion(m1->Lsommet,malla,nsol,solucion,m1->Ltriangle,tinterp);
}
#endif /* ADAPT2D */

#ifndef ADAPT2D
if (nfunc > 0)
    *interp_func  = interpolacion(m1->Lsommet,malla,nfunc,func,m1->Ltriangle,tinterp);
#endif /* ADAPT2D */

#ifdef DEBUG_FILE
  cout<<"Mesh saving."<<endl;
  cout<<"------------"<<endl<<endl;
  strcpy(fic0,timesh);
  strcat(fic0,"-debug.am_fmt");
  err=m1->write(fic0);
#endif /* DEBUG_FILE */  

  t_fin=m1->write();

  delete m1;
  return t_fin;
}


