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

int mshsu(Triangulo_T1_dlist* Ltriangle, Arista_T1_dlist* Larete,\
          Triangulo_T1_dlist* t_vec,Triangulo_T1_dlist* t_loc,\
          Vertice_T1* so, Metrica m0, int angulo,Mallado_T0* malla,
          CAD* cad,int prof) {
        
  Triangulo_T1_dlist* tsu;
  Triangulo_T1_dlink* t_aux,*lt,*lt0,*lt_lk;
  Arista_CAD_dlink* a_fin,*a0_fin;
  Arista_T1_dlink* a_aux;
  Triangulo_T0* t0_su,*t00_su,*tri0=malla->saca_triangle(0);
  Triangulo_T1* t0,*ts,*tp;
  Triangulo_T1* trian;
  Triangulo_T1* triansu;
  Arista_T1* ar,*a0,*a1;
  Arista_T1 aa0;
  Vertice_T1* su=NIL;
  Vertice_T1* ssu;
  Metrica mm,mtr0;
  R2 cor[3];
  R2 pt,co_su,pt_pr;
  Scalar det;
  Scalar crit,crit_min=0;
  int t_ini,t_fin;
  int ref_su;
  int as,ap,aa;
  int pos;
  int i,k;
  int p_pos[3];
  int* kt,*jt;
  int err=1,err0;
  int num,cont=0;
  Boolean diff=FALSE;
  
  p_pos[0]=1;p_pos[1]=2;p_pos[2]=0;
  
  if (so->ref>=0) { //Si tiene ref. negativa no se puede suprimir
    // busco el vertice que esta mas cercano al que quiero 
    // suprimir.
    num=t_vec->num_elem();
    trian=new Triangulo_T1[num];
    if (trian==NIL) ERROR();
    su=t_vec->suprime(so,trian,malla,m0,angulo,aa0);
    if (su) { // No es NIL 
      if (aa0.contiene(so,su)==FALSE) {
        cerr<<"Error in mshsu."<<endl;
        cerr<<"Edge not containg vertices."<<endl;
        cerr<<"a0:"<<aa0<<endl;
        cerr<<"v0:"<<*so<<endl;
        cerr<<"v1:"<<*su<<endl;
        exit(1);
      }
      tsu=new Triangulo_T1_dlist;
      if (tsu==NIL) ERROR();          
      t_vecinos(su,*tsu);
      num=tsu->num_elem();
      triansu=new Triangulo_T1[num];
      ssu=new Vertice_T1;
      if (triansu==NIL || ssu==NIL) ERROR();
      (*ssu)=(*su);
      lt_lk=tsu->principio();
      cont=0;
      while (lt_lk) {
        triansu[cont]=*(lt_lk->t);
        pos=triansu[cont].sommet(su);
        triansu[cont].s[pos]=ssu;
        cont++;
        lt_lk=lt_lk->sig();
      }  
      ref_su=su->ref;
      t0_su=su->t0;
      t00_su=su->t0;
      mtr0=su->mtr;
      co_su=su->c;
      a0_fin=su->a;
      if ((su->front==TRUE && so->front==FALSE) || (su->ref<0) || \
          (su->inters==TRUE && so->inters==FALSE)) {
        pt=su->c;
      }
      else {
        if (su->front==TRUE || su->inters==TRUE) {
          pt=su->c;
        }
        else {
          pt=(so->c+su->c)*.5;            
          if (aa0.front==TRUE|| aa0.inters==TRUE) {
            if (aa0.inters==TRUE) 
              a_fin=proyeccion_cao(cad,pt,pt_pr,su->a,2,prof);
            else
              a_fin=proyeccion_cao(cad,pt,pt_pr,su->a,1,prof);
            pt=pt_pr;
          }
          su->c=pt;
          t_ini=((su->t0)-tri0);
          su->mtr=malla->metrica(pt,t_ini,t_fin);
          su->t0=malla->saca_triangle(t_fin);
        }
      }
      
      num=t_vec->num_elem();                    
      kt= new int[num];
      jt= new int[num];
      if (kt==NIL || jt==NIL) ERROR();
      cont=0;
      t_aux=t_vec->principio();
      while (t_aux) {
        t0=t_aux->t;
        for (i=0; i<3; i++) {
          if (t0->s[i]==su) {
            if (t0->ref==0)
              t0->ref=-999;
            else
              t0->ref=-t0->ref;
          }
          else {
            if (t0->s[i]==so)
              kt[cont]=i;
            else
              jt[cont]=i;
          }          
        }
        t_aux=t_aux->sig();
        cont++;
      }
      cont=0;
      t_aux=t_vec->principio();
      while (t_aux) {
        t0=t_aux->t;
        if (t0->ref>=0) {
          t0->s[kt[cont]]=su;
          det=t0->area2D();
          if (det<=0) {
            diff=TRUE;
            break;
          }
          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);
          crit=criter(cor[0],cor[1],cor[2],mm);
          if (crit<=crit_min) {
            diff=TRUE;
            break;
          }
          t0->krit=crit;
        }
        else {
          as=t0->arista(so,t0->s[jt[cont]]);
          ap=t0->arista(su,t0->s[jt[cont]]);
          ts=t0->t[as];
          tp=t0->t[ap];
          if (ts==NIL && tp==NIL){
            diff=TRUE;
            break;
          }
        }
        cont++;
        t_aux=t_aux->sig();
      }
      if (diff!=TRUE && (co_su!=(su->c))) {
        cont=0;
        lt_lk=tsu->principio();
        while (lt_lk) {
          t0=lt_lk->t;
          if (t0->ref>=0) {
            det=t0->area2D();
            if (det<=0) {
              diff=TRUE;
              break;
            }
            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);
            crit=criter(cor[0],cor[1],cor[2],mm);
            if (crit<=crit_min) {
              diff=TRUE;
              break;
            }
            t0->krit=crit;
          }
          cont++;
          lt_lk=lt_lk->sig();
        }
      } 
      delete ssu;
      delete tsu;
      delete[] triansu;
      if (diff==TRUE) {
        su->ref=ref_su;
        su->c=co_su;
        su->t0=t00_su;
        su->mtr=mtr0;
        su->a=a0_fin;
        cont=0;
        t_aux=t_vec->principio();
        while (t_aux) {
          *(t_aux->t)=trian[cont];
          t_aux=t_aux->sig();
          cont++;
        }
      }
      else {
        //if (so->ref>0 && su->ref>=0) su->ref=so->ref;
        cont=0;
        t_aux=t_vec->principio();
        while (t_aux) {
          t0=t_aux->t;
          if (t0->ref<0) {
            if (su->t==t0) su->t=NIL;
            aa=t0->arista(so,su);
            ar=t0->a[aa];
            as=t0->arista(so,t0->s[jt[cont]]);
            ap=t0->arista(su,t0->s[jt[cont]]);
            ts=t0->t[as];
            tp=t0->t[ap];
            if (ts) {
              aa=ts->arista(t0->a[as]);
              ts->t[aa]=tp;
              ts->a[aa]=t0->a[ap];
              t0->a[ap]->tr=ts;
            }
            else {
              cout<<"Atencion. mshsu."<<endl;
              t0->a[ap]->front=TRUE;
              t0->a[ap]->ref=t0->a[as]->ref;
              t0->a[ap]->tr=tp;
              
            }
            if (tp) {
              aa=tp->arista(t0->a[ap]);
              tp->t[aa]=ts;
              t0->a[ap]->s[0]->t=tp;
              t0->a[ap]->s[1]->t=tp;
            }
            a_aux=t0->a[as]->lk;  
            Larete->kill(a_aux); //borro la arista que sobra
            
            
          }
          t_aux=t_aux->sig();
          cont++;
        }
        
        // cout<<"entro para arreglar aristas"<<endl;
        t_aux=t_vec->principio();
        while (t_aux) {
          t0=t_aux->t;
          if (t0->ref>=0) {
            if (su->t==NIL) su->t=t0;
            for (i=0; i<3; i++) {
              t0->s[i]->t=t0;
              if (t0->s[i]==su) {
                a0=t0->a[i];
                a1=t0->a[p_pos[p_pos[i]]];
                for (k=0; k<2; k++) {
                  if (a0->s[k]==so) a0->s[k]=su;
                  if (a1->s[k]==so) a1->s[k]=su;
                }
                //a0->m_ref();
                //a1->m_ref();
              }
            }
          }
          t_aux=t_aux->sig();
        }
        a_aux=ar->lk;  // quito la arista so,su
        Larete->kill(a_aux);
        
//         cout<<"entro a optimizacion"<<endl;
        t_aux=t_vec->principio();
        while (t_aux) {
          t0=t_aux->t;
          if (t0->ref<0) {
            lt=t0->lk;
            lt0=t0->lk0;
            Ltriangle->kill(lt); // suprimo el triangulo de la lista.
//               cout<<"KILL:"<<lt0<<endl;
            t_loc->kill(lt0);
            delete t0;
          }
          t_aux=t_aux->sig();
        }
        err=0;
      }
      delete[] kt;
      delete[] jt;
    }
    delete[] trian;
  }
  
  return err;
}
