#include <stdio.h>
#include <math.h>
#include <string.h>
#include "graphic3d.h"
#include "es.h"
#include "util.h"
#include "trans.h"

#define T_PI 3.14159265358979
 
static triangle_t ListDep[NB];
triangle_t *ListTri;    /* Liste de triangles avec des sommets en (x,y,z) */
int         NbTri;      /* Nombre de triangles a generer                  */

point3d_t *ListSmt;     /* Liste de sommets distincts         */
int        NbSmt;       /* Nombre de sommets distincts        */

int *ListTrie_x;
int *ListTrie_y;
double *ListTheta;

static double rayon=1.;
static double rmin;
static char   outf[80]="";
static int    depth;

triangleInd_t *ListInd; /* Liste de triangles avec des sommets en indices */

/***************************************************************/
/* Allocation et initialisation d'un tableau de triangle       */
/* La taille finale = nb * echelle, nb etant le nombre de      */
/* triangles initiales et echelle le multiplicateur de la      */
/* subdivision                                                 */ 
/***************************************************************/
triangle_t * Initialisation( triangle_t *list_init, int nb, int echelle)
{
  triangle_t *list_tri;  /* Tableau de triangles */
  int         i;         /* indice d'iterations  */

  list_tri = (triangle_t *) malloc( nb*echelle*sizeof(triangle_t) );

  if ( !list_tri ) {
    fprintf( stdout, "Erreur d'allocation de memoire : %d octets\n",
	     nb*echelle*sizeof(triangle_t) );
    exit(-2);
  }

  for ( i=0; i<nb; i++ ) {
    CopyTriangle( list_tri[i*echelle], list_init[i] );
  }

  return list_tri;
}


/***************************************************************/
/* Subdivision d'un seul triangle                              */
/***************************************************************/
void  SubdivisionSingle( triangle_t t0, 
			 triangle_t t1, triangle_t t2,
			 triangle_t t3, triangle_t t4 )
{
  triangle_t tmp;
  point3d_t  p01, p12, p30;

  CopyTriangle( tmp, t0 );

  /* Calcul des points de milieu */
  p01.x = t0[0].x + t0[1].x;
  p01.y = t0[0].y + t0[1].y;
  p01.z = t0[0].z + t0[1].z;

  p12.x = t0[1].x + t0[2].x;
  p12.y = t0[1].y + t0[2].y;
  p12.z = t0[1].z + t0[2].z;

  p30.x = t0[0].x + t0[2].x;
  p30.y = t0[0].y + t0[2].y;
  p30.z = t0[0].z + t0[2].z;

  Normaliser( &p01 );
  Normaliser( &p12 );
  Normaliser( &p30 );

  /* Creation de nouveaux triangles */
  CopyPoint3d( t1+0, tmp+0 );
  CopyPoint3d( t1+1, &p01 );
  CopyPoint3d( t1+2, &p30 );

  CopyPoint3d( t2+0, &p01 );
  CopyPoint3d( t2+1, tmp+1 );
  CopyPoint3d( t2+2, &p12 );

  CopyPoint3d( t3+0, &p01 );
  CopyPoint3d( t3+1, &p12 );
  CopyPoint3d( t3+2, &p30 );

  CopyPoint3d( t4+0, &p30 );
  CopyPoint3d( t4+1, &p12 );
  CopyPoint3d( t4+2, tmp+2 );
}



/************************************************************* */
/* Affinement du maillage d'une spere unitaire par une         */
/* subdivision recursive des triangles                         */
/************************************************************* */
void SubdivisionGlobale( triangle_t *list_tri, int nb, int prof )
{
  int p;           /* indice d'iteration de la profondeur */
  int i;           /* indice des triagnles a subdiviser   */
  int nb_total=nb; /* nb * 4^prof                         */
  int pas_cur=1, pas_suiv=1;

  for ( i=0; i<prof; i++ ) pas_suiv *= 4;

  nb_total = nb * pas_suiv;

  for ( p=prof; p>0; p-- ) {
    pas_cur = pas_suiv;
    pas_suiv /= 4;

    for ( i=0; i<nb_total; i+=pas_cur )
      SubdivisionSingle( list_tri[i], 
			 list_tri[i], list_tri[i+pas_suiv], 
			 list_tri[i+2*pas_suiv], list_tri[i+3*pas_suiv] );
  }
}


/************************************************************* */
/* Calcul de la profondeur de la subdivision                   */
/* a partir du rayon de la sphere R et rmin                    */
/************************************************************* */
int calculDepth( double R, double r, int nb_dep )
{
  double   surf_s;     /* surface de la sphere  */
  double   surf_t;     /* surface d'un triangle */
  int      nb_tri;
  int      depth = 1;

  surf_s = 4.0 * M_PI * rayon * rayon;
  surf_t = sqrt( (double)3.0 ) * M_PI * rmin * rmin / 4.0;
  nb_tri =  (int)(surf_s / surf_t + 0.5);

printf("Nb de triangles minimum : %d\n", nb_tri);
  nb_tri = (int)( (double)nb_tri/nb_dep + 0.5);

  while ( nb_tri>4 ) {
    nb_tri = (int) ( (double)nb_tri/4.0 + 0.5 );
    depth++;
  }

  return depth;
}


/************************************************************* */
/* Analyse des options du programme                            */
/************************************************************* */
void analyzeParams(int argc, char **argv)
{
  int ind_p=0, ind_o=0;   /* indicateurs des options -p et -o */ 
  int ind_R=0, ind_r=0;   /* indicateurs des options -R et -r */ 
  int i=0;

  while ( ++i<argc ) {
    if ( strcmp( argv[i], "-d" ) == 0 ) { 
      depth = atoi( argv[++i] );
      ind_p = 1;
    }

    if ( strcmp( argv[i], "-o" ) == 0 ) {
      strcpy( outf, argv[++i] );
      ind_o = 1;
    }

    if ( strcmp( argv[i], "-R" ) == 0 ) {
      rayon = atof( argv[++i] );
      ind_R = 1;
    }

    if ( strcmp( argv[i], "-r" ) == 0 ) {
      rmin = atof( argv[++i] );
      ind_r = 1;
    }
  }

  if ( !ind_p ) {
    if ( !ind_R || !ind_r ) {
      fprintf( stderr, "Usage : %s -d profondeur[-R rayon -r rmin] -c [-i] -cart [-spher] -o fichier\n", argv[0] );
      fprintf( stderr, "Les options -R et -r doivent etre utilisees ensemble\n" );
      exit( -1 );
    } 
    else {
      printf( "R = %f, r=%f\n", rayon, rmin );
      depth = calculDepth( rayon, rmin, NB );
    }
  }

  if ( !ind_o ) {
      fprintf( stderr, "Manque -o fichier\n" );
      exit( -1 );
  }
}

main( int argc, char **argv )
{
	FILE    *fp;
	int      i=0;
	int      size=1;
	int tab_x, tab_y, j, coord;
	double d_i, angle_phi, angle_theta, angle_ri;
  
	if ( argc == 1 ) {
		fprintf( stderr, "Usage : %s -d profondeur[-R rayon -r rmin] -c [-i] -cart [-spher] -o fichier\n", argv[0] );
		exit( -1 );
	}
	
	if (strcmp(argv[3], "-cart") == 0)
		coord = 0;
	else if (strcmp(argv[3], "-spher") == 0)
		coord = 1;


	/* Analyse des options entrees                              */
	analyzeParams( argc, argv );

	printf( "profondeur = %d", depth);

	/* Calcul du nombre de triangles a generer                  */
	for ( i=0; i<depth; i++ ) size *= 4;

	/* Transformation des triangles avec des indices de sommets */
	/* en coordonnees des sommets                               */
	Indices2Points( tindices, ListDep, NB );

	/* Initialisation de la liste avec les triangles de depart  */
	printf("Initialisation ... \n" );
	ListTri = Initialisation( ListDep, NB, size);

	printf("Subdivision : profondeur %d ...\n", depth );
	SubdivisionGlobale( ListTri, NB, depth );

	NbTri = NB*size;
	
	/* Transformation d'une liste de triangles des coordonnees 3D   */
    /* en une liste de sommets et une liste de triangles des indices */
    ListInd = (triangleInd_t *) malloc( NbTri*sizeof(triangleInd_t) );
	
	ListSmt =(point3d_t *) malloc( NbTri*3 / 4 * sizeof(point3d_t) );
    ListCoord2Ind( ListTri, NbTri, ListSmt, &NbSmt, ListInd  );
    printf("Nombre de sommets distincts :%d\n", NbSmt);
	
	/*	Tri des coordonnŽes des sommets	*/
	ListTrie_x =(int *) malloc( NbSmt * sizeof(int) );
	ListTrie_y =(int *) malloc( NbSmt * sizeof(int) );
	ListTheta =(double *) malloc( NbSmt * sizeof(double) );
	if (coord == 1)
	{
		printf("Tri des coordonnees spheriques des sommets\n");
		for (i=0; i<NbSmt; i++)
		{
			d_i = sqrt(ListSmt[i].x*ListSmt[i].x+ListSmt[i].y*ListSmt[i].y);
			if (ListSmt[i].z == 0)
				angle_theta = T_PI/2.0;
			else 
				angle_theta = atan2(d_i,ListSmt[i].z);
			ListTheta[i] = angle_theta;			
		}
		for (i=0; i<NbSmt; i++)
		{
			tab_x=0;
			for (j=0; j<i; j++)
			{
				if (ListTheta[j]<=ListTheta[i])
					tab_x ++;
			}
			for (j=(i+1); j<NbSmt; j++)
			{
				if (ListTheta[j]<ListTheta[i])
					tab_x ++;
			}
			ListTrie_x[tab_x] = i;
		}
		printf("Ecriture du fichier\n");
		PrintFichIndSpher( ListSmt, NbSmt, ListInd, NbTri, ListTrie_x, ListTheta, outf );
	}
	else
	{	
		printf("Tri des coordonnees cartesiennes des sommets\n");
		for (i=0; i<NbSmt; i++)
		{
			tab_x=0;
			tab_y=0;
			for (j=0; j<i; j++)
			{
				if (ListSmt[j].x<=ListSmt[i].x)
					tab_x ++;
				if (ListSmt[j].y<=ListSmt[i].y)
					tab_y ++;
			}
			for (j=(i+1); j<NbSmt; j++)
			{
				if (ListSmt[j].x<ListSmt[i].x)
					tab_x ++;
				if (ListSmt[j].y<ListSmt[i].y)
					tab_y ++;
			}
			ListTrie_x[tab_x] = i;
			ListTrie_y[tab_y] = i;
		}
		printf("Ecriture du fichier\n");
		PrintFichIndCart( ListSmt, NbSmt, ListInd, NbTri, ListTrie_x, ListTrie_y, outf );
	}

	

	free(ListSmt);
	free(ListInd);
	free(ListTrie_x);
	free(ListTrie_y);
	free(ListTheta);
	
}