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

void Triangulo_T1_dlist::write (ostream& os) {
  int num=0;
  num=this->num_elem();
  Triangulo_T1_dlink* aux;
  aux=begin;
  os <<endl<< "Numero de Triangulos"<<num<<endl;
  os << endl
     << "       ***** TRIANGULOS *****"<<endl
     << "             ----------"<<endl;
  
  while (aux) {
    os <<aux<<" "<<(*(aux->t))<<endl;
    aux=aux->suc;
  }
}
ostream& operator<<(ostream& os, Triangulo_T1_dlist& rrl)
{rrl.write(os); return os;}



   
/*   
                       SUBRRUTINA CONTIENE
     
     Determina si un vertice dado por *so esta en la lista de triangulos.
     1=Si su esta en la lista.
     0=Si no esta.
*/
  
Boolean Triangulo_T1_dlist::contiene(Vertice_T1* so) {
  Triangulo_T1_dlink* aux;
  Boolean encontrado=FALSE;
  if (begin) {
    aux=begin;
    while (aux) {
      if((aux->t)->sommet0(so)!=-1) {
        encontrado=TRUE;
        break;
      }
      aux=aux->sig();
    }
  }
  return encontrado;
}


/*
                      SUBRRUTINA SUPRIME
     
      Dictamina si el vertice so puede suprimirse o no.
      En caso afirmativo devuelve el vertice con el que se va
      a identificar (su) y el array trian con los triangulos
      correspondientes, junto con la arista so-su.
       Esta subrrutina se llama en mshsu.C
     

*/
Vertice_T1* Triangulo_T1_dlist::suprime(Vertice_T1* so,\
          Triangulo_T1 trian[], Mallado_T0* malla_metr,\
          Metrica m0,int angulo1, Arista_T1& aa0) {

  typedef Arista_T1* Parist;
  typedef Triangulo_T1* Ptr;
  
  Triangulo_T1_dlink* t_aux;
  Triangulo_T1* t0,*tini,*t1;
  Ptr* ttr;
  Arista_T1* a0,*a1;
  Parist* aret;
  Vertice_T1* su=NIL;
  Vertice_T1* ss;
  R2 c0,c1;
  Scalar dist=1e+36,d0;
  int s1,cont=0,num;
  int ang=0,j,angg;
  int ref0,ref1,refini;
  int p_pos[3],p_inv[3];
  Boolean frontera=FALSE;
  p_pos[0]=1;p_pos[1]=2;p_pos[2]=0;
  p_inv[0]=2;p_inv[1]=0;p_inv[2]=1;
  
  c0=so->c;
  if (angulo1>=10) 
    angg=10;
  else
    angg=angulo1;

  if (so->inters==TRUE) {
    frontera=FALSE;
    num=this->num_elem();
    aret=new Parist[num];
    ttr=new Ptr[num];
    if (aret==NIL && ttr==NIL) ERROR();
    num=0;
    t_aux=this->principio();
    tini=t_aux->t;
    a0=tini->a[(tini->sommet(so))];
    frontera=a0->front;
    refini=tini->ref;
    cont=0;
    ref0=refini;
    t0=tini;
    trian[cont]=*t0;
    cont++;
    while (t_aux) {
      t_aux=t_aux->sig();
      if (t_aux) {
        t1=t_aux->t;
        ref1=t1->ref;
        trian[cont]=*t1;
        cont++;
      }
      else {
        if (frontera==FALSE){
          ref1=refini;
        }
        else {
          ref1=ref0;
        }
      }
      if (ref0!=ref1) {
        ttr[num]=t0;
        aret[num]=t0->a[p_inv[(t0->sommet(so))]];
        num++;
      }
      ref0=ref1;      
      t0=t1;
    }
    if (frontera==TRUE && num>0) {
      delete[] aret;
      delete[] ttr;
      return su;
    }
    else {
      if (num>0 && num!=2) {
        delete[] aret;
        delete[] ttr;
        return su;
      }
      if (num==2) {
        ang=int(angle(aret[0],aret[1]));
        if (ang>angg) {
          delete[] aret;
          delete[] ttr;
          return su;
        }
        else {
          for (j=0;j<2; j++) {
            ss=aret[j]->s[0];
            c1=aret[j]->s[0]->c;
            if (ss==so) {
              c1=aret[j]->s[1]->c;
              ss=aret[j]->s[1];
            }
            d0=distance(c0,m0,c1,ss->mtr);
            if (d0<dist) {
              dist=d0;
              su=ss;
              aa0=*(aret[j]);
            }
          }
          delete[] aret;
          delete[] ttr;
          return su;
        }
      }
    }
    delete[] aret;
    delete[] ttr;
  }
  else {
    cont=0;
    t_aux=this->principio();
    while (t_aux) {
      t0=t_aux->t;
      trian[cont]=*t0;
      s1=t0->sommet_sig(so);
      c1=t0->s[s1]->c;
      d0=distance(c0,m0,c1,t0->s[s1]->mtr);
      if (d0<dist) {
        if (so->front==TRUE) {
          if (t0->a[t0->arista(so,t0->s[s1])]->front==TRUE) { 
            dist=d0;
            su=t0->s[s1];
            aa0=*(t0->a[t0->arista(so,su)]);
          }
        }
        else {
          dist=d0;
          su=t0->s[s1];
          aa0=*(t0->a[t0->arista(so,su)]);
        }
      }
      t_aux=t_aux->sig();
      cont++;
    }
    s1=p_pos[s1];
    if (t0->t[s1]==NIL) {
      c1=t0->s[s1]->c;
      d0=distance(c0,m0,c1,t0->s[s1]->mtr);
      if (d0<dist) {
        if (so->front==TRUE) {
          if (t0->a[t0->arista(so,t0->s[s1])]->front==TRUE) { 
            dist=d0;
            su=t0->s[s1];
            aa0=*(t0->a[t0->arista(so,su)]);
          }
        }
        else {
          dist=d0;
          su=t0->s[s1];
          aa0=*(t0->a[t0->arista(so,su)]);
        }
      }
    }
  }
  if (su) { // No es NIL
    if (aa0.front==TRUE) { //arista en la frontera.
      t_aux=this->principio();
      t0=t_aux->t;
      a0=t0->a[t0->sommet(so)];
      t_aux=this->fin();
      t0=t_aux->t;
      a1=t0->a[p_pos[p_pos[t0->sommet(so)]]];
      ang=int(angle(a1,a0));
      if (ang>=angg) {
        su=NIL;
        return su;
      }
    }
  }
  return su;
}

/*
                      SUBRRUTINA TEST_SUPRIME
     
      Dictamina si el vertice so puede suprimirse o no.
      Esta subrrutina se llama en mshsar.C
     

*/
Boolean  Triangulo_T1_dlist::test_suprime(Vertice_T1* so, Vertice_T1* su,\
         Arista_T1* aa0,int angulo1) {

  typedef Arista_T1* Parista;
  
  Triangulo_T1_dlink* t_aux;
  Triangulo_T1* t0,*tini,*t1;
  Arista_T1* a0,*a1;
  Parista* aret;
  int num;
  int ang=0,angg;
  int p_pos[3],p_inv[3];
  int pos0,pos1;
  int ref0,ref1,refini;
  Boolean frontera=FALSE;
  Boolean suprimir=FALSE;
  p_pos[0]=1;p_pos[1]=2;p_pos[2]=0;
  p_inv[0]=2;p_inv[1]=0;p_inv[2]=1;
  
  if (angulo1>=10)
    angg=10;
  else
    angg=angulo1;
  if ((aa0->front==FALSE && aa0->inters==FALSE) && \
      ( (su->front==TRUE && so->front==TRUE) || \
        (su->front==TRUE && so->inters==TRUE) ||
        (su->inters==TRUE && so->front==TRUE) ||
        (su->inters==TRUE && so->inters==TRUE))) {
    return suprimir;
  }
  
  if (so->front==TRUE && so->inters==FALSE && \
      su->front==FALSE && su->inters==TRUE) {
    return suprimir;
  }
  if (so->inters==TRUE && so->front==FALSE && \
      su->inters==FALSE && su->front==TRUE) {
    return suprimir;
  }
  if (aa0->front==TRUE) { //arista en la frontera.
    t_aux=this->principio();
    t0=t_aux->t;
    a0=t0->a[t0->sommet(so)];
    t_aux=this->fin();
    t0=t_aux->t;
    a1=t0->a[p_pos[p_pos[t0->sommet(so)]]];
    ang=int(angle(a1,a0));
    if (ang>=angg) {
      return suprimir;
    }
  }
  if (so->inters==TRUE) {
    frontera=FALSE;
    num=this->num_elem();
    aret=new Parista[num];
    if (aret==NIL) ERROR();
    num=0;
    t_aux=this->principio();
    tini=t_aux->t;
    a0=tini->a[(tini->sommet(so))];
    frontera=a0->front;
    refini=tini->ref;
    ref0=refini;
    t0=tini;
    while (t_aux) {
      t_aux=t_aux->sig();
      if (t_aux) {
        t1=t_aux->t;
        ref1=t1->ref;
      }
      else {
        if (frontera==FALSE) {
          ref1=refini;
        }
        else {
          ref1=ref0;
        }
      }
      if (ref0!=ref1) {
        aret[num]=t0->a[p_inv[(t0->sommet(so))]];
        num++;
      }
      ref0=ref1;      
      t0=t1;
    }
    if (frontera==TRUE && num>0) {
      delete[] aret;
      return suprimir;
    }
    else {
      if (num>0 && num!=2) {
        delete[] aret;
        return suprimir;
      }
      if (num==2) {
        ang=int(angle(aret[0],aret[1]));
        if (ang>angg) {
          delete[] aret;
          return suprimir;
        }
        else {                 
          pos0=aret[0]->vertice(su);
          pos1=aret[1]->vertice(su);
          if (pos1<0 && pos0<0)  {
            delete[] aret;
            return suprimir;
          }  
        }
      }  
    }
    delete[] aret;
  }

  suprimir=TRUE;
  return suprimir;
}


void Triangulo_T1_dlist::areas() {

  Triangulo_T1_dlink* lt_lk;
  Scalar area;
  
  lt_lk=this->principio(); 
  while (lt_lk) {
    area=lt_lk->t->area2D();
    if (area<=0) {
      cout<<"Area negativa"<<endl;
      cout<<*(lt_lk->t);
      exit(1);
    }
    lt_lk=lt_lk->sig();
  }
}
