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


#include <header.hxx>
#include <t_t1_dl.hxx>
#include <a_t1_dl.hxx>
#include <v_t1_dl.hxx>
#include <front_t1.hxx>
#include <m_t0.hxx>
#include <cad.hxx>

extern Scalar criter(R2,R2,R2,Metrica);
extern int mshopt(Triangulo_T1*,const int&);
int mshsu(Triangulo_T1_dlist*, Arista_T1_dlist*, Triangulo_T1_dlist*,
          Triangulo_T1_dlist*,Vertice_T1*, Metrica, int,Mallado_T0*,CAD*,int);
extern t_vecinos(Vertice_T1*,Triangulo_T1_dlist&);          
extern t_local(Vertice_T1*,Triangulo_T1_dlist&);
void   mshreg0(Triangulo_T1_dlist*, Vertice_T1*,Scalar,Scalar,Mallado_T0*);
void   mshreg1(Triangulo_T1_dlist*, Vertice_T1*,Scalar,Mallado_T0*,Scalar,
               int,Scalar,Frontera_T1*);

void filtrado (Triangulo_T1_dlist* Ltriangle,Arista_T1_dlist* Larete,
	       Vertice_T1_dlist* Lsommet,Mallado_T0* malla,int angulo,
               int num_iter,Boolean filtro,Scalar hwall,int refwall,
               Boolean fluid,CAD* cad,int prof) {

  Frontera_T1* frontera=NIL;
  Triangulo_T1_dlist* t_vec, *t_loc;
  Triangulo_T1_dlink* Lt_lk;
  Triangulo_T1* t0;
  Arista_T1_dlink* La_lk;
  Arista_T1* a0;
  Vertice_T1_dlink* Ls_lk, *Ls_auxk;
  Vertice_T1* so;
  Metrica mm;
  R2 cor[3];
  Scalar omega1=.4;
  Scalar owall;
  Scalar crit_min;
  int err = 0,nu,err0;
  int i,aa;
  int num=0;
  int ps[2];
  int nb_capas=2;
  ps[0]=1;ps[1]=0;
#ifdef DEBUG  
  cout<<"Mesh Regularization."<<endl;
  cout<<"--------------------"<<endl<<endl;
#endif /* DEBUG */
  if (filtro==TRUE) {
    Ls_lk=Lsommet->principio();
    while (Ls_lk) {
      so=Ls_lk->s;
      if (so->front==FALSE && so->inters==FALSE) {
        t_vec=new Triangulo_T1_dlist;
        t_loc=new Triangulo_T1_dlist;
        if (t_vec==NIL || t_loc==NIL) ERROR();
        t_vecinos(so,*t_vec);
        t_local(so,*t_loc);
        nu=t_vec->num_elem();
        if (nu<=4) {
	  err=mshsu(Ltriangle,Larete,t_vec,t_loc,so,so->mtr,angulo,malla,cad,prof); 
	  if (err==0) {
	    Ls_auxk=Ls_lk->sig();
	    Lsommet->kill(Ls_lk);
	    Ls_lk=Ls_auxk;
	  }
	  else {
	    Ls_lk=Ls_lk->sig();
	  }
        }
        else Ls_lk=Ls_lk->sig();
        delete t_vec;
        Lt_lk=t_loc->principio();
        while (Lt_lk) {
	  t0=Lt_lk->t;
	  t0->lk0=NIL;
	  if (err==0) {
	    mshopt(t0,0);
	    mshopt(t0,1);
	    mshopt(t0,2);
	  }  
	  Lt_lk=Lt_lk->sig();
        }
        delete t_loc;
        
      }
      else Ls_lk=Ls_lk->sig();
    } 
  }

  La_lk=Larete->principio();
#ifdef DEBUG
  cout<<endl<<"Swapping.............."<<endl;
#endif /* DEBUG */
  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<<"Atencion. Error en Sub. filtrado.C."<<endl;
	cerr<<"Punteros aristas-triangulos defectuosos."<<endl;
	exit(1);
      }
      else {
	mshopt(t0,aa);
      }
    }
    La_lk=La_lk->sig();
  }
 
  if (fluid==TRUE) {
    frontera=Larete->crea_frontera();
    if (frontera==NIL) {
      cerr<<"Error. No se pudo crear frontera_T1."<<endl;
      cerr<<"Sub. filtrado.C"<<endl;
      exit(1);
    }
    for (i=0; i<nb_capas; i++) {
      La_lk=Larete->principio();
      while (La_lk) {
	a0=La_lk->a;
	if (a0->front==FALSE){
	  switch (i) {
	  case 0: {
	    if (abs(a0->s[0]->ref)==refwall && a0->s[1]->front==FALSE) {
	      if (a0->s[1]->fluid==FALSE) a0->s[1]->fluid=1;
	    }
	    else {
	      if (abs(a0->s[1]->ref)==refwall && a0->s[0]->front==FALSE) {
		if (a0->s[0]->fluid==FALSE) a0->s[0]->fluid=1;
	      }
	    }
	    break;
	  }
	  default: {
	    if (a0->s[0]->fluid==i && a0->s[1]->front==FALSE) {
	      if (a0->s[1]->fluid==FALSE) a0->s[1]->fluid=i+1;
	    }
	    else {
	      if (a0->s[1]->fluid==i && a0->s[0]->front==FALSE) {
		if (a0->s[0]->fluid==FALSE) a0->s[0]->fluid=i+1;
	      }
	    }
	    break;
	  }
	  }
	}
	La_lk=La_lk->sig();
      }
    }
  } 
  for (num=0; num<num_iter; num++) {
    Ls_lk=Lsommet->principio();
    while (Ls_lk) {
      so=Ls_lk->s;
      crit_min=1e+30;
      if  (so->front==FALSE && so->inters==FALSE && so->ref>=0) { 
	t_vec=new Triangulo_T1_dlist;
	if (t_vec==NIL) ERROR();
	t_vecinos(so,*t_vec);
	Lt_lk=t_vec->principio();
	while (Lt_lk) {
	  t0=Lt_lk->t;
	  if (t0->krit==-999) {
	    mm.set(0,0,0);
	    for (i=0;i<3;i++) {
	      mm=mm+(t0->s[i]->mtr.inv(err0))/3.0;
	      cor[i]=t0->s[i]->c;
	    } 
	    mm=mm.inv(err0);
	    t0->krit=criter(cor[0],cor[1],cor[2],mm);  
	  }
	  if (t0->krit<crit_min) crit_min=t0->krit;
	  Lt_lk=Lt_lk->sig();
	}
	if (crit_min<.8) {
	  mshreg0(t_vec,so,omega1,crit_min,malla);      
	}
	Ls_lk=Ls_lk->sig();
	delete t_vec;             
      }
      else Ls_lk=Ls_lk->sig();
    }
  }
  if (fluid==TRUE) {
    for (num=0; num<num_iter; num++) {
      owall=.5+.5*Scalar(num+1)/Scalar(num_iter);
      Ls_lk=Lsommet->principio();
      while (Ls_lk) {
	so=Ls_lk->s;
	crit_min=1e+30;
	if  (so->fluid!=FALSE) { 
	  t_vec=new Triangulo_T1_dlist;
	  if (t_vec==NIL) ERROR();
	  t_vecinos(so,*t_vec);
	  crit_min=1e-4;
	  mshreg1(t_vec,so,crit_min,malla,hwall,refwall,owall,frontera);      
	  if (t_vec) {delete t_vec; t_vec=NIL;}
	}
	Ls_lk=Ls_lk->sig();
      }
    }
  }
  if (frontera) {delete frontera;frontera=NIL;}
     
}
