
#include "spline.h"
#include "zones.h"


char *concatener(void *,long,void *,long,size_t);


/******************************************************************************/
/* SELECTION D'UNE PARTIE DE LA TEXTURE ET INSERTION DANS UNE AUTRE           */
/*                                                                            */
/* ENTREES :                                                                  */
/*   -> vaC      : variables aleatoires utilisees pour la synthese            */
/*   -> zonesC   : zones presentes dans la texture courante                   */
/*   -> vaS      : variables aleatoires a inserer (insertion)                 */
/*   -> zonesS   : zones presentes dans la texture a inserer (insertion)      */
/*   -> points   : points de controle de la spline (selection)                */
/* SORTIES :                                                                  */
/*   -> va       : variables aleatoires utilisees pour la synthese            */
/*   -> zones    : zones presentes dans la texture courante                   */
/******************************************************************************/

void mexFunction(int nb_sorties,      mxArray *sorties[],
                 int nb_entrees,const mxArray *entrees[]) {

  /* variables locales */

  char action,stop;
  long nC,limite_shaperC,nb_points,nS=0,nb_zonesC,nb_zonesS,nb_shaper_moins=0,
       nb_formesC,nb_ellipsesC,nb_shapers_quelconquesC,cpt=0,n1,n2,n3,n4,i,j;
  double *sx,*sy,sup_x,sup_y,inf_x,inf_y,x,y;
  tirage_t *vaC,*vaS,*va_temp,*va;
  en_tete_t *en_teteC,*en_teteS;
  zone_t *zonesC,*zonesS,*zones;
  forme_t *formesC,*formesS,*formes;
  ellipse_t *ellipsesC,*ellipsesS,*ellipses;
  shaper_t *shapersC,*shapersS,*shapers;
  polynome_t *polynomes_x,*polynomes_y;


  /* lecture des entrees */

  if (nb_entrees!=3 && nb_entrees!=4) mexErrMsgTxt("Il faut trois ou quatre entrees !");
  if (nb_sorties!=2) mexErrMsgTxt("Il faut deux sorties !");
  action=nb_entrees-2;
  vaC=(tirage_t *)mxGetPr(entrees[0]);
  nC=mxGetN(entrees[0]);
  en_teteC=(en_tete_t *)mxGetPr(entrees[1]);
  limite_shaperC=en_teteC->indice_debut_non_shaper;
  if (action==1) { /* selection */
    if (mxGetN(entrees[2])!=2 || mxGetM(entrees[2])<3)
      mexErrMsgTxt("La matrice des points de la forme est invalide !");
    nb_points=mxGetM(entrees[2]);
    sx=mxGetPr(entrees[2]);
    sy=mxGetPr(entrees[2])+nb_points;
  } else { /* insertion */
    vaS=(tirage_t *)mxGetPr(entrees[2]);
    nS=mxGetN(entrees[2]);
    en_teteS=(en_tete_t *)mxGetPr(entrees[3]);
  }


  /* lecture des listes de zones et construction de la courbe spline qui */
  /* delimite la selection                                               */

  nb_zonesC=lire_liste_zones(en_teteC,&zonesC,&formesC,&ellipsesC,&shapersC);
  if (action==2) { /* insertion */
    nb_zonesS=lire_liste_zones(en_teteS,&zonesS,&formesS,&ellipsesS,&shapersS);
    nb_points=zonesS[0].nb_points_controle;
    sx=zonesS[0].points_x;
    sy=zonesS[0].points_y;
  }
  polynomes_x=(polynome_t *)malloc((nb_points-1)*sizeof(polynome_t));
  polynomes_y=(polynome_t *)malloc((nb_points-1)*sizeof(polynome_t));
  coeffs_spline(sx,polynomes_x,nb_points);
  coeffs_spline(sy,polynomes_y,nb_points);


  /* determination d'un cadre pour la selection : determination du plus */
  /* petit rectangle dans lequel est incrite la selection               */

  sup_x=sup_y=-INFINI;
  inf_x=inf_y= INFINI;
  for (i=0;i<nb_points-1;i++) {
    bornes(polynomes_x[i],sx[i],&inf_x,&sup_x);
    bornes(polynomes_y[i],sy[i],&inf_y,&sup_y);
  }


  /* selection des variables aleatoires : points conserves s'ils sont dans */
  /* la forme pour la selection ou hors de la forme pour l'insertion       */

  for (i=0;i<nb_zonesC;i++) zonesC[i].nb_elements=0;
  va_temp=(tirage_t *)malloc(nC*sizeof(tirage_t));
  for (i=0;i<nC;i++) {
    x=vaC[i].x; y=vaC[i].y;
    if ((action==1 && x>=inf_x && x<=sup_x && y>=inf_y && y<=sup_y &&
          appartient_forme(polynomes_x,polynomes_y,sx,sy,nb_points,x,y)) ||
        (action==2 && (x<inf_x || x>sup_x || y<inf_y || y>sup_y ||
         !appartient_forme(polynomes_x,polynomes_y,sx,sy,nb_points,x,y)))) {
      va_temp[cpt]=vaC[i];
      zonesC[va_temp[cpt].numero_zone].nb_elements++;
      cpt++;
    } else if (i<limite_shaperC) {
      nb_shaper_moins++;
    }
  }


  /* suppression des zones non representees (il n'existe plus de multiplieurs dans cette zone) */
  
  nb_formesC             =en_teteC->nb_formes;
  nb_ellipsesC           =en_teteC->nb_ellipses;
  nb_shapers_quelconquesC=en_teteC->nb_shapers_quelconques;
  i=0;
  while (i<nb_zonesC) {
    if (zonesC[i].nb_elements==0) {
      stop=0;
      if (i>0) for (j=i+1;j<nb_zonesC && !stop;j++) {
        if (zonesC[j].masque==i) { /* si la zone n'a plus de representants mais que */
          zonesC[i].mort=1;        /* l'on a besoin de la garder comme masque       */
          stop=1;
        }
      }
      if (!stop)
        supprimer_zone(zonesC,&nb_zonesC,i,formesC,&nb_formesC,ellipsesC,&nb_ellipsesC,
                       shapersC,&nb_shapers_quelconquesC,va_temp,cpt,0);
      else i++;
    } else i++;
  }


  /* modification du numero des motifs dans la matrice des variables aleatoires */
  /* selectionnees (insertion). Puisque la position des zones inserees dans la  */
  /* liste est modifiee (decalage vers la droite) le lien des multiplieurs vers */
  /* les parametres de leur motif aussi (spline, polygone, shaper, ...)         */

  if (action==2) {
    for (i=0;i<nS;i++) {
      vaS[i].numero_zone+=nb_zonesC;
      if (vaS[i].type_motif==m_polygone || vaS[i].type_motif==m_spline)
        vaS[i].numero_motif+=nb_formesC;
      else if (vaS[i].type_motif==m_ellipse)
        vaS[i].numero_motif+=nb_ellipsesC;
      else if (vaS[i].type_motif==m_shaper_quelconque)
        vaS[i].numero_motif+=nb_shapers_quelconquesC;
    }
  }


  /* nouvelle matrice des variables aleatoires */

  sorties[0]=mxCreateDoubleMatrix(sizeof(tirage_t)/sizeof(double),nS+cpt,mxREAL);
  va=(tirage_t *)mxGetPr(sorties[0]);
  if (action==1) memcpy(va,va_temp,cpt*sizeof(tirage_t)); /* selection */
  else { /* insertion */
  
    /* recopie des variables aleatoires dans la nouvelle liste */
    /* en stockant en tete les motifs shaper                   */

    n1=en_teteC->indice_debut_non_shaper-nb_shaper_moins;
               /* shapers de la liste courante         */
    n2=en_teteS->indice_debut_non_shaper;
               /* shapers de la liste selectionnee     */
    n3=cpt-n1; /* non shapers de la liste courante     */
    n4=nS -n2; /* non shapers de la liste selectionnee */
    memcpy(va         ,va_temp   ,n1*sizeof(tirage_t));
    memcpy(va+n1      ,vaS       ,n2*sizeof(tirage_t));
    memcpy(va+n1+n2   ,va_temp+n1,n3*sizeof(tirage_t));
    memcpy(va+n1+n2+n3,vaS    +n2,n4*sizeof(tirage_t));
  }


  /* nouvelle liste des zones */

  if (action==1) { /* selection */
  
    /* on affecte a la zone en tete de liste (la plus ancienne des zones selectionees), */
    /* les contours de la selection en tant que limite de zone                          */

    zonesC[0].nb_points_controle=nb_points;
    zonesC[0].points_x=sx;
    zonesC[0].points_y=sy;


    /* on indique que cette zone constitue un masque pour les autres zones (lorsque */
    /* l'on tire des nouveaux multiplieurs dans ces zones, il ne doivent pas etre   */
    /* tires en dehors du masque)                                                   */

    for (i=1;i<nb_zonesC;i++)
      if (zonesC[i].masque==-1) zonesC[i].masque=0;


    construire_liste_zones(nb_zonesC,nb_formesC,nb_ellipsesC,nb_shapers_quelconquesC,
                           en_teteC->indice_debut_non_shaper-nb_shaper_moins,
                           zonesC,formesC,ellipsesC,shapersC,sorties+1,&en_teteC);
    liberer_memoire_zones(en_teteC,zonesC,formesC,ellipsesC,shapersC,0);

  } else { /* insertion */
  
    /* on indique aux zones inserees que leur masque a ete decale dans la liste des zones */

    for (i=0;i<nb_zonesS;i++)
      if (zonesS[i].masque!=-1) zonesS[i].masque+=nb_zonesC;
      

    /* concatenation des deux listes de zones */

    zones   =(zone_t *)   concatener(zonesC   ,nb_zonesC   ,zonesS   ,nb_zonesS            ,sizeof(zone_t)   );
    formes  =(forme_t *)  concatener(formesC  ,nb_formesC  ,formesS  ,en_teteS->nb_formes  ,sizeof(forme_t)  );
    ellipses=(ellipse_t *)concatener(ellipsesC,nb_ellipsesC,ellipsesS,en_teteS->nb_ellipses,sizeof(ellipse_t));
    shapers =(shaper_t *) concatener(shapersC ,nb_shapers_quelconquesC,shapersS,en_teteS->nb_shapers_quelconques,
                                     sizeof(shaper_t));
    construire_liste_zones(nb_zonesC+nb_zonesS,nb_formesC+en_teteS->nb_formes,nb_ellipsesC+en_teteS->nb_ellipses,
                           nb_shapers_quelconquesC+en_teteS->nb_shapers_quelconques,n1+n2,zones,formes,ellipses,
                           shapers,sorties+1,&en_teteC);
    liberer_memoire_zones(en_teteC,zones,formes,ellipses,shapers,0);
  }


  /* liberation de la memoire */

  free(polynomes_x);
  free(polynomes_y);
  free(va_temp);
}


/******************************************************************************/
/* CONCATENATION DE DEUX VECTEURS                                             */
/******************************************************************************/

char *concatener(void *gauche,long nb_gauche,void *droite,long nb_droite,
                 size_t taille_elements) {

  /* variables locales */
  
  char *retour=(char *)malloc((nb_gauche+nb_droite)*taille_elements);


  memcpy(retour                          ,gauche,nb_gauche*taille_elements);
  memcpy(retour+nb_gauche*taille_elements,droite,nb_droite*taille_elements);
  free(gauche); free(droite);

  return retour;
}

