/*
 * 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 <mshap.hxx>

int mshap(Triangulo_T1_dlist* Ltriangle, Arista_T1_dlist* Larete, \
          Vertice_T1_dlist* Lsommet,Triangulo_T1* t,Arista_T1* a,\
          Mallado_T0* malla,CAD* cad,int prof)
          
{ 
  Triangulo_T1_dlink* Lt_lk;
  Triangulo_T1* t0,*t00,*t1,*t11;
  Triangulo_T1* tv0,*tv1,*tv2,*tv3,*tv4,t2;
  Triangulo_T0* tri0,*tf;
  Arista_CAD_dlink* a_fin;
  Arista_T1_dlink* La_lk;
  Arista_T1* a0,*a1,*a2,*b0,*b1,*ar0,*ar1,*ar2;
  Vertice_T1_dlink* Ls_lk;
  Vertice_T1* s0,*s1,*s2,*s3,*so,ss;
  Metrica mp,mm,m0i,m1i,m2i,m3i,mpi;
  R2 c0,c1,pt,pt_ini,pt_pr;
  Scalar d2,d3,area1,area2,area3,area4;
  Scalar crit0,crit00,crit1,crit11;
  Scalar crit_min=0.003;
  int p[3];
  int err=1,err0;
  int s10,s11,s12,s20,s21,s22;
  int a10,a11,a12,a20,a21,a22;
  int aa,posa,refp=0,cont,ang=0;
  int t_ini,t_fin;
  Boolean add=TRUE;
  p[0]=1;p[1]=2;p[2]=0;
  
  tri0=malla->saca_triangle(0); //puntero inicial de triangulos_T0
  
  t0=t;
  a0=a;
  a10=t0->arista(a0);
  s10=a10;
  s0=t0->s[a10];
  s1=a0->s[1];
  posa=1;
  if (s1==s0) {
    s1=a0->s[0];
    posa=0;
  }
  c0=s0->c;
  c1=s1->c;
  //dd=lg/Scalar(int(lg+.5));
  //if (a0->frontera()==TRUE || a0->interseccion()==TRUE) dd=0.5*lg;
  pt=c0-(c0-c1)*0.5;
  if (a0->frontera()) { // arista en la frontera
    s11=p[s10];
    s12=p[s11];
    s1=t0->s[s11];
    s2=t0->s[s12];
    a11=p[a10];
    a12=p[a11];
    
    a1=t0->a[a11];
    a2=t0->a[a12];
    
    tv0=NIL;
    tv1=t0->t[a11];
    tv2=t0->t[a12];
    refp=a0->ref;
//       refp=s0->ref;
//       if (refp<0 && refp!=-999) refp=-refp;
//       if (refp==-999) refp=0;


       
/*     modificamos el triangulo t0 y creamos un nuevo triangulo t00;

                 s2                                 s2 
                 x                                  x     
                / \                                /|\
               /   \                              / | \
              /     \                            /  |  \
             /       \                          /   |   \
     tv2 a2 /         \ a1 tv1                 /    |    \
           /   t0      \               tv2 a2 /     |ar2  \ a1 tv1
          /             \                    /      |      \
         /               \                  /   t0  |  t00  \
        /                 \                /        |        \
    s0 x-------------------x s1           x---------+---------x
                a0                       s0    a0   so   ar0  s1
                tv0=NIL                        tv0=NIL
*/
    cont=0;
    area1=area2=crit0=crit00=0;
    pt_ini=pt;
    while (cont<2 &&(area1<=0 || area2<=0 || crit0<=0 || crit00<=0 || ang>=cad->angul())) {
      if (cont==0) {
        a_fin=proyeccion_cao(cad,pt,pt_pr,s0->a,1,prof);
        pt=pt_pr;
	ang=int(angle(pt-a0->s[0]->c,a0->s[1]->c-pt));
      }
      else
        pt=pt_ini;    
      cont++;
      ss.set(pt,refp,s0->front,t0,s0->t0);
      t_ini=((s0->t0)-tri0);
      mp=malla->metrica(pt,t_ini,t_fin);
      mpi=mp.inv(err0);
      m0i=s0->mtr.inv(err0);
      m1i=s1->mtr.inv(err0);
      m2i=s2->mtr.inv(err0);
      tf=malla->saca_triangle(t_fin);
      t2.set(&ss,s1,s2,NIL,NIL,NIL,NIL,NIL,NIL,0);
      //mm=(mp+s1->mtr+s2->mtr)/3.0;
      mm=((mpi+m1i+m2i)/3.0).inv(err0);
      crit00=criter(pt,s1->c,s2->c,mm);
      if (crit00<crit_min) crit00=0;
      area1=t2.area2D();
      t2.set(s0,&ss,s2,NIL,NIL,NIL,NIL,NIL,NIL,0);
      mm=(s0->mtr+mp+s2->mtr)/3.0;
      mm=((m0i+mpi+m2i)/3.0).inv(err0);
      crit0=criter(s0->c,pt,s2->c,mm);
      if (crit0<crit_min) crit0=0;
      area2=t2.area2D();
    }
    if (area1>0 && area2>0 && crit0>0 && crit00>0) {
      t00=new Triangulo_T1;
      if (t00==NIL) ERROR();
      so=new Vertice_T1;
      if (so==NIL) ERROR();
      so->set(pt,refp,TRUE,t0,tf);
      so->mtr=mp;
      so->a=a_fin;
      ar2=new Arista_T1;
      if (ar2==NIL) ERROR();
      ar2->set(s2,so,t0,0,FALSE);
      ar0=new Arista_T1;
      if (ar0==NIL) ERROR();
      ar0->set(so,s1,t00,a0->ref,TRUE);
      //ar0->m_ref();
      
      a0->s[posa]=so;
      t0->s[a11]=so;
      t0->a[a11]=ar2;
      t0->t[a11]=t00;
      t0->krit=crit0;
      
      t00->set(so,s1,s2,tv0,tv1,t0,ar0,a1,ar2,t0->ref);
      t00->krit=crit00;
      s1->t=t00;
      a1->tr=t00;
      if (tv1) {
//          cout<<"mshap 2. arista"<<endl;
        aa=tv1->arista(a1);
        tv1->t[aa]=t00;
      }
      
      Ls_lk=new Vertice_T1_dlink;
      if (Ls_lk==NIL) ERROR();
      so->set_lk(Ls_lk);
      Ls_lk->set(so,NIL,NIL);
      Lsommet->append(Ls_lk);
      
      La_lk=new Arista_T1_dlink;
      if (La_lk==NIL) ERROR();
      ar0->set_lk(La_lk);
      La_lk->set(ar0,NIL,NIL);
      Larete->append(La_lk);
      La_lk=new Arista_T1_dlink;
      if (La_lk==NIL) ERROR();
      ar2->set_lk(La_lk);
      La_lk->set(ar2,NIL,NIL);
      Larete->append(La_lk);
      
      Lt_lk=new Triangulo_T1_dlink;
      if (Lt_lk==NIL) ERROR();
      t00->set_lk(Lt_lk);
      Lt_lk->set(t00,NIL,NIL);
      Ltriangle->append(Lt_lk);
      
/*
  optimizacion local del mallado despues de la modificacion
  
  */
      if (a1->front==FALSE && a1->inters==FALSE) {
        err=mshopt(t0,a12);
      }
      if (a2->front==FALSE && a2->inters==FALSE) {
        err=mshopt(t00,1);
      }
      err=0;      
    }  
  }
  else { // La arista no esta en la frontera.

/*     modificamos los triangulos t0 y t1. Creamos los triangulos
       t00 y t11

                 s2                                 s2 
                 x                                  x
                / \                                /|\
               /   \                              / | \
              /     \                            /  |  \
             /       \                          /   |   \
     tv2 a2 /         \ a1 tv1                 /    |    \
           /   t0      \               tv2 a2 /     |ar2  \ a1 tv1
          /             \                    /      |      \
         /               \                  /   t0  |  t00  \
        /       a0        \                /    a0  |  ar0   \
    s0 x-------------------x s1        s0 x---------+---------x s1
        \                 /                \        so       /
         \               /                  \       |       /
          \     t1      /                    \  t1  |  t11 /
   tv3  b0 \           /  b1 tv4       tv3 b0 \     |ar1  / b1 tv4
            \         /                        \    |    /
             \       /                          \   |   /
              \     /                            \  |  /  
               \   /                              \ | /
                \ /                                \|/
                 x                                  x
                 s3                                 s3

*/
    t1=t0->t[a10];
    s11=p[s10];
    s12=p[s11];
    s1=t0->s[s11];
    s2=t0->s[s12];
    a11=p[a10];
    a12=p[a11];
    
    a1=t0->a[a11];
    a2=t0->a[a12];
    
    
    tv1=t0->t[a11];
    tv2=t0->t[a12];
//       cout<<"mshap 3. arista"<<endl;
    a20=t1->arista(a0);
    a21=p[a20];
    a22=p[a21];
    b0=t1->a[a21];
    b1=t1->a[a22];
    s3=t1->s[a22];
    s20=t1->sommet(s1);
    s21=p[s20];
    s22=p[s21];
    tv3=t1->t[a21];
    tv4=t1->t[a22];
    refp=a0->ref;
    if (a0->inters==TRUE) {
      a_fin=proyeccion_cao(cad,pt,pt_pr,s0->a,1,prof);
      pt=pt_pr;
    }
    t_ini=((s0->t0)-tri0);
    mp=malla->metrica(pt,t_ini,t_fin);
    mpi=mp.inv(err0);
    m0i=s0->mtr.inv(err0);
    m1i=s1->mtr.inv(err0);
    m2i=s2->mtr.inv(err0);
    m3i=s3->mtr.inv(err0);
    tf=malla->saca_triangle(t_fin);       
    ss.set(pt,refp,FALSE,t0,s0->t0);
    t2.set(&ss,s1,s2,NIL,NIL,NIL,NIL,NIL,NIL,0);
    //mm=(mp+s1->mtr+s2->mtr)/3.0;
    mm=((mpi+m1i+m2i)/3.0).inv(err0);
    crit00=criter(pt,s1->c,s2->c,mm);
    if (crit00<crit_min) crit00=0;
    area1=t2.area2D();
    t2.set(s1,&ss,s3,NIL,NIL,NIL,NIL,NIL,NIL,0);
    //mm=(s1->mtr+mp+s3->mtr)/3.0;
    mm=((m1i+mpi+m3i)/3.0).inv(err0);
    crit11=criter(s1->c,pt,s3->c,mm);
    if (crit11<crit_min) crit11=0;
    area2=t2.area2D();
    t2.set(s0,&ss,s2,NIL,NIL,NIL,NIL,NIL,NIL,0);
    //mm=(s0->mtr+mp+s2->mtr)/3.0;
    mm=((m0i+mpi+m2i)/3.0).inv(err0);
    crit0=criter(s0->c,pt,s2->c,mm);
    if (crit0<crit_min) crit0=0;
    area3=t2.area2D();
    t2.set(&ss,s0,s3,NIL,NIL,NIL,NIL,NIL,NIL,0);
    //mm=(mp+s0->mtr+s3->mtr)/3.0;
    mm=((mpi+m0i+m3i)/3.0).inv(err0);
    crit1=criter(pt,s0->c,s3->c,mm);
    if (crit1<crit_min) crit1=0;
    area4=t2.area2D();
    if (area1>0 && area2>0 && area3>0 && \
        area4>0 && crit0>0 && crit00>0 && \
        crit1>0 && crit11>0)  {
      d2=distance(pt,s0->mtr,s2->c,s2->mtr);
      d3=distance(pt,s0->mtr,s3->c,s3->mtr);
      if ((d2<.6 || d3<.6) && ((a0->interseccion())==FALSE)) {
        err=mshrt(t0,a10);
        if (err==2) {
          add=FALSE;
//                cerr<<"se produjo vuelta"<<endl;
        }
      }
      if (add) {
        t00=new Triangulo_T1;
        t11=new Triangulo_T1;
        so=new Vertice_T1;
        if (t00==NIL || t11==NIL || so==NIL) ERROR();
        so->set(pt,refp,FALSE,t0,tf);
        so->mtr=mp;
        so->a=a_fin;
        if (a0->interseccion()) so->inters=TRUE;
        
        ar2=new Arista_T1;
        if (ar2==NIL) ERROR();
        ar2->set(s2,so,t0,0,FALSE);
        ar0=new Arista_T1;
        if (ar0==NIL) ERROR();
        ar0->set(so,s1,t00,0,FALSE);
        if (a0->interseccion()) ar0->inters=TRUE;
        ar1=new Arista_T1;
        if (ar1==NIL) ERROR();
        ar1->set(so,s3,t11,0,FALSE);
        a0->s[posa]=so;
        t0->s[s11]=so;
        t0->a[a11]=ar2;
        t0->t[a11]=t00;
        t0->krit=crit0;
        
        t00->set(so,s1,s2,t11,tv1,t0,ar0,a1,ar2,t0->ref);
        t00->krit=crit00;
        s1->t=t00;
        a1->tr=t00;
        if (tv1) {
//             cout<<"mshap 4. arista"<<endl;
          aa=tv1->arista(a1);
          tv1->t[aa]=t00;
        }
        
        t1->s[s20]=so;
        t1->a[a22]=ar1;
        t1->t[a22]=t11;
        t1->krit=crit1;
        t11->set(s1,so,s3,t00,t1,tv4,ar0,ar1,b1,t1->ref);
        t11->krit=crit11;
        b1->tr=t11;
        if (tv4) {
//             cout<<"mshap 5. arista"<<endl;
          aa=tv4->arista(b1);
//             cout<<"fin 5. arista"<<endl;
          tv4->t[aa]=t11;
        }
        Ls_lk=new Vertice_T1_dlink;
        if (Ls_lk==NIL) ERROR();
        so->set_lk(Ls_lk);
        Ls_lk->set(so,NIL,NIL);
        Lsommet->append(Ls_lk);
        
        La_lk=new Arista_T1_dlink;
        if (La_lk==NIL) ERROR();
        ar0->set_lk(La_lk);
        La_lk->set(ar0,NIL,NIL);
        Larete->append(La_lk);
        La_lk=new Arista_T1_dlink;
        if (La_lk==NIL) ERROR();
        ar2->set_lk(La_lk);
        La_lk->set(ar2,NIL,NIL);
        Larete->append(La_lk);
        La_lk=new Arista_T1_dlink;
        if (La_lk==NIL) ERROR();
        ar1->set_lk(La_lk);
        La_lk->set(ar1,NIL,NIL);
        Larete->append(La_lk);
        
        Lt_lk=new Triangulo_T1_dlink;
        if (Lt_lk==NIL) ERROR();
        t00->set_lk(Lt_lk);
        Lt_lk->set(t00,NIL,NIL);
        Ltriangle->append(Lt_lk);
        Lt_lk=new Triangulo_T1_dlink;
        if (Lt_lk==NIL) ERROR();
        t11->set_lk(Lt_lk);
        Lt_lk->set(t11,NIL,NIL);
        Ltriangle->append(Lt_lk);
        
/*
  optimizacion local del mallado despues de la modificacion
  
  */
        if (a2->front==FALSE && a2->inters==FALSE) {
          err=mshopt(t0,a12);
        }
        if (a1->front==FALSE && a1->inters==FALSE) {
          err=mshopt(t00,1);
        }
        if (b0->front==FALSE && b0->inters==FALSE) {
          err=mshopt(t1,a21);
        } 
        if (b1->front==FALSE && b1->inters==FALSE) {
          err=mshopt(t11,2);
        }
        err=0;
      }
    }
  }
  return err;
}


