#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "vecmath.h"
#include "laminate.h"


/*
  Function : sphere_laminate

  Arguments : ---- 2DO

  Comment :
    -- Make a set of transformations (called 'laminations')
    -- In a first time, transformations are uniformally done, without range ... but it'll change ...
 */
void sphere_laminate (sphere_t *sphere, vector *normal, vector *center, int n_plane, float step) {
  int i, j ;
  int *hits ;
  int minv, maxv, diffv ;
  float level = 0.1 ;
  
  vector dest ;

  hits = malloc (sizeof (int) * sphere -> n_vertices) ;

  for (i = 0 ; i < sphere -> n_vertices ; i++)
    hits[i] = 0 ;

  for (i = 0 ; i < n_plane ; i++) {
    for (j = 0 ; j < sphere -> n_vertices ; j++) {
      float scalar ;
      vector dest ;
      //      float istep = (0.2 + ((i / (float) n_plane) * 0.8)) * step ;

      dest = center[i] ;
      vector_scale (&dest, -1) ;
      vector_add (&dest, sphere -> vertices[j].position) ;
      //     vector_normalize (&dest) ;
      scalar = (dest.x * normal[i].x) + (dest.y * normal[i].y) + (dest.z * normal[i].z) ;

      /*     dest = sphere -> vertices[j].position ;
	     vector_normalize (&dest) ;*/

      if (scalar < 0) {
	//vector_scale (&dest, -istep) ;
	hits[j] ++ ;
      }
      else {
	//vector_scale (&dest, istep) ;
	hits[j] -- ;
      }

      //      vector_add (&sphere -> vertices[j].position, dest) ;
    }
    //    if ((((i*100) / n_plane) % 100) == 0) printf ("%f %\n", (i/((float) n_plane)) * 100) ;
  }

  minv = maxv = hits[0] ;
  for (i = 0 ; i < sphere -> n_vertices ; i++) {
    if (hits[i] < minv) minv = hits[i] ;
    if (hits[i] > maxv) maxv = hits[i] ;
  }

  diffv = maxv - minv ;

  if (diffv != 0) {
    for (i = 0 ; i < sphere -> n_vertices ; i++) {
      float lv = (hits[i] - minv) / (float) diffv ;
      
      lv = (lv * 2) - 1 ;
      
      dest = sphere -> vertices[i].position ;
      vector_normalize (&dest) ;
      
      vector_scale (&dest, level * lv) ;
      //  printf ("(%f, %f, %f)\n", dest.x, dest.y, dest.z) ;
      vector_add (&sphere -> vertices[i].position, dest) ;
    }
  }
  
  free (hits) ;

  printf ("\n") ;
}

void sphere_computeminmax (sphere_t *sphere, float *min, float *max) {
  int i ; 
  float tmp ;

  for (i = 0 ; i < sphere -> n_vertices ; i++) {
    tmp = vector_norm (&sphere -> vertices[i].position) ;
    if (tmp < (*min)) *min = tmp ;
    if (tmp > (*max)) *max = tmp ;
  }
}

void sphere_blurnorm (sphere_t *sphere) {
  int i ;
  int j ;
  int k ;
  float mean ;
  int n_faces ;

  for (i = 0 ; i < sphere -> n_vertices ; i++) {
    mean = vector_norm (&(sphere -> vertices[i].position)) * 0.5 ;
    //   printf ("old : %f, ", mean) ;
    n_faces = (sphere -> vertices[i].triangles[4] == -1)?4:6 ;

    for (j = 0 ; j < n_faces ; j++) {
      triangle_p triangle = sphere -> triangles + (sphere -> vertices[i].triangles[j]) ;
      
      for (k = 0 ; k < 3 ; k ++)
	if (triangle -> vertices[k] != i)
	  mean += vector_norm(&(sphere -> vertices[triangle -> vertices[k]].position)) ;
    }
    mean /= (n_faces * 2) + 0.5 ;
    //   printf ("new : %f\n", mean) ;
    vector_normalize (&(sphere -> vertices[i].position)) ;
    vector_scale (&(sphere -> vertices[i].position), mean) ;
  }
}

void sphere_colorize (sphere_t *sphere, float min, float max) {
  float diff = max - min ;
  float localmean ;
  float col ;
  int i ;

  for (i = 0 ; i < sphere -> n_vertices ; i++) {
    localmean = vector_norm (&sphere -> vertices[i].position) ;
      
    col = (localmean - min)/ diff ;
      
    if (col < 0.45) { // Mer
      col /= 0.45 ;
      vertex_setcolor (sphere->vertices+i, 
		       0.1 + (0.5*col),
		       0.1 + (0.5*col),
		       0.9 + (0.1*col),
		       1) ;
    }
    else { // Terre
      col = (col - 0.45) / 0.55 ;
      
      if (col < 0.1) { // Plage
	col /= 0.1 ;
	vertex_setcolor (sphere->vertices+i,
			 0.6 + (0.2*col),
			 0.6 + (0.2*col),
			 1 - col,
			 1) ;
      }
      else {
	if (col < 0.8) { // Plaine, Falaise
	  col = (col - 0.1) / 0.7 ;
	  vertex_setcolor (sphere->vertices+i,
			   0.3 + (0.4*col),
			   0.5 + (0.2*col),
			   0.7 * col,
			   1) ;
	}
	else { // Sommet
	  col = (col - 0.8) / 0.2 ;
	  vertex_setcolor (sphere->vertices+i,
			   0.74 + (0.24*col) ,
			   0.74 + (0.23*col) ,
			   0.74 + (0.22*col) ,
			   1) ;
	}
      }
    }  
  }
}

void sphere_reweight (sphere_t *sphere, float min, float max) {
  float diff = max - min ;
  int i ;
  float localmean ;
  vector vtmp ;
  float tmp ;

  for (i = 0 ; i < sphere -> n_vertices ; i++) {
    tmp = vector_norm (&sphere -> vertices[i].position) ;
    vtmp = sphere -> vertices[i].position ;
    vector_normalize (&vtmp) ;
    
    localmean = (tmp - min) / diff ;
    
    if (localmean < 0.55) {
      localmean = 1 + ((localmean - 0.5) * 0.0095) ;
    }
    else {
      localmean = 1 + ((localmean - 0.5) * 0.35) ;
    }
    
    vector_scale (&vtmp, localmean) ;
    sphere -> vertices[i].position = vtmp ;
  }
}


void sphere_computenormals (sphere_t *sphere) {
  int i ;
  
  
  for (i = 0 ; i < sphere -> n_triangles ; i++) {
    vector_computenormal 
      (&sphere->triangles[i].normal,
       sphere->vertices[sphere->triangles[i].vertices[0]].position,
       sphere->vertices[sphere->triangles[i].vertices[1]].position,
       sphere->vertices[sphere->triangles[i].vertices[2]].position) ;
  }
  
  for (i = 0 ; i < sphere -> n_vertices ; i++)
    sphere_computevertexnormal (sphere -> vertices + i, sphere) ;
}

void sphere_adjust   (sphere_t *sphere) {
  /*
    Detail     Nb Lamin      Lamin Method    Step
      
   */
  adjustparam_t params[6] = { 
    { 0.00005,        0,        0.19 },     // 100
    { 0.0000005,       0,        0.12 },     // 420
    { 0.000027,       0,        0.09 },     // 1700
    { 0.0000045,      500,        0.2 },     // 6820
    { 0.0001,        500,        0.02 },     // 27300
    { 0.00002,       500,        0.2 }      // 109200
  } ;
  int i, j ;
  vector *center ;
  vector *normal ;

  float min = 4000000.0, max = -1 ;

  srand (time(NULL)) ;
  center = malloc (sizeof (vector) * 50000) ;
  normal = malloc (sizeof (vector) * 50000) ;

  for (i = 0 ; i < 6 ; i++) {
    printf ("Level : %d\n", i) ;

    sphere_detail (sphere) ;

    for (j = 0 ; j < params[i].n_lamination ; j++) {
      normal[j].x = (rand()%2000) - 1000 ;
      normal[j].y = (rand()%2000) - 1000 ;
      normal[j].z = (rand()%2000) - 1000 ;
      //     normal[j].h = 1 ;
      vector_normalize (normal + j) ;

      center[j].x = (rand()%200) - 100 ;
      center[j].y = (rand()%200) - 100 ;
      center[j].z = (rand()%200) - 100 ;

      vector_normalize (&(center[j])) ;
      vector_scale (&(center[j]), params[i].centerdist) ;
    }

    sphere_laminate (sphere, normal, center, params[i].n_lamination, params[i].step) ;
  }

  free (center) ;
  free (normal) ;

  sphere_computeminmax (sphere, &min, &max) ;
  
  sphere_colorize (sphere, min, max) ;
  
  sphere_reweight (sphere, min, max) ;
  
  sphere_blurnorm (sphere) ;

  sphere_computenormals (sphere) ;

}
