#include "vor_bitmap.h"
#include "background.h"
#include "color.h"

VoronoiDiagramBitmap::VoronoiDiagramBitmap(double *vx, int nb_vx, double epsilon, int maxsize, Background *bg)
{
    _bg = bg;

    double *s, *send; // pointer used to travel on |s|
    double xmin, xmax, ymin, ymax; // bounding box
    double dx, dy; // extra size enlarged by |epsilon|
    
    s = vx;
 
    nv = nb_vx;    
    
    send = vx + 2*nv /* previously: + ptind(n)*/;
    xmin = *s++;
    ymin = *s++;
    xmax = xmin;
    ymax = ymin;

    while (s < send) // creation de la boite englobante, c'est a dire que boucle sur tous les sommets (t -> t+2*n)
    {
        if (*s < xmin)
            xmin = *s;
        else if (*s > xmax)
            xmax = *s;
        ++s;
        if (*s < ymin)
            ymin = *s;
        else if (*s > ymax)
            ymax = *s;
        ++s;
    }
    
    dx = (xmax - xmin) * epsilon; // ajout du facteur epsilon a la taille de la boite englobante
    xmin -= dx;
    xmax += dx;
    dy = (ymax - ymin) * epsilon;
    ymin -= dy;
    ymax += dy;

    for (int i = 0; i < nv; i++)
    {
        Vertex*pv = get_vertex(i);
      	pv->p[0]= vx[2*i];
	pv->p[1]= vx[2*i+1];
	pv->a = _bg->get_angle(pv->p);
    }
    
    if (xmax-xmin > ymax-ymin)
    {
      nx = maxsize;
      dl = (xmax-xmin)/(double)nx;
      ny = (int)floor((ymax-ymin)/dl)+1;
      double midy = (ymax+ymin)/2.0;
      ymin = midy - (double)ny*dl/2.0;
      ymax = midy + (double)ny*dl/2.0;
    }
    else
    {
      ny = maxsize;
      dl = (ymax-ymin)/(double)ny;
      nx = (int)floor((xmax-xmin)/dl)+1;
      double midx = (xmax+xmin)/2.0;
      xmin = midx - (double)nx*dl/2.0;
      xmax = midx + (double)nx*dl/2.0;
    }
    
    _parray = new Pixel[nx*ny];
    
    min[0] = xmin;
    min[1] = ymin;
    
    _range_max = nv;
}

VoronoiDiagramBitmap::~VoronoiDiagramBitmap()
{
  delete _parray;
}

void VoronoiDiagramBitmap::generate()
{
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);
	    pix->_closest = -1;
	    pix->_d = INFINITY;
	    pix->bit_flag = 0x0;
	}      
    }
  
    for (int v = _init; v < _end; v++)
    {
        Vertex *pv = get_vertex(v);
		
	double p[2];
	
        for (int j = 0; j < ny; j++)
        {
            for (int i = 0; i < nx; i++)
            {
		Pixel *pix = get_pixel(i, j);
		get_coord(i, j, p);
		
		double d = distance(pv->p, pv->a, p);
		pix->set_closest(v, d);
            }
        }
    }
}

// void VoronoiDiagramBitmap::f(int v0, int v1, int v2, double *value)
// {
//   	    double d0 = distance(get_vertex(v0)->p, get_vertex(v0)->a, p);
// 	    double d1 = distance(get_vertex(v1)->p, get_vertex(v1)->a, p);
// 	    double d2 = distance(get_vertex(v2)->p, get_vertex(v2)->a, p);
// 	    *value = (d0-d1)*(d0-d1) + (d0-d2)*(d0-d2) + (d1-d2)*(d1-d2);
// }
// 
// void VoronoiDiagramBitmap::df(int v0, int v1, int v2, double *value)
// {
//   	    double d0 = distance(get_vertex(v0)->p, get_vertex(v0)->a, p);
// 	    double d1 = distance(get_vertex(v1)->p, get_vertex(v1)->a, p);
// 	    double d2 = distance(get_vertex(v2)->p, get_vertex(v2)->a, p);
// 	    *value = (d0-d1)*(d0-d1) + (d0-d2)*(d0-d2) + (d1-d2)*(d1-d2);
// }

void VoronoiDiagramBitmap::generate_func(int v0, int v1, int v2)
{  
    double dist_inc= 0.005;
    double linewidth = 0.0005;
    
    _range_max = 0;
    
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);
	    pix->_closest = -1;
	    pix->_d = INFINITY;
	    pix->bit_flag = 0x0;
	}      
    }
  	
    double p[2];
    for (int j = 0; j < ny; j++)
    {
	for (int i = 0; i < nx; i++)
	{
	    Pixel *pix = get_pixel(i, j);
	    get_coord(i, j, p);
	    
	    double d0 = distance(get_vertex(v0)->p, get_vertex(v0)->a, p);
	    double d1 = distance(get_vertex(v1)->p, get_vertex(v1)->a, p);
	    double d2 = distance(get_vertex(v2)->p, get_vertex(v2)->a, p);
	    double d = (d0-d1)*(d0-d1) + (d0-d2)*(d0-d2) + (d1-d2)*(d1-d2);
	    int range = (int)floor(d/dist_inc);
	    pix->set_closest(range, d);
	    if (range > _range_max)
	      _range_max = range;
	}
    }
    
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);
            double d = pix->_d;
            if (fmod(d, dist_inc) >= 0.0 && fmod(d, dist_inc) <= linewidth)
                pix->set_flag(ISOLINE, 1);
        }
    }
}

// void VoronoiDiagramBitmap::find_vor_center(int v0, int v1, int v2)
// {
//   double start[2] = {0.0, 0.0};
//   
//   start[0] += get_vertex(v0)->p[0];
//   start[0] += get_vertex(v1)->p[0];
//   start[0] += get_vertex(v2)->p[0];
//   start[0] /= 3.0;
//   
//   start[1] += get_vertex(v0)->p[1];
//   start[1] += get_vertex(v1)->p[1];
//   start[1] += get_vertex(v2)->p[1];
//   start[1] /= 3.0;
//   
//   double step[2];
//   while()
//   {
//     
//   }  
// }
  

void VoronoiDiagramBitmap::add_points(int point_size)
{
    for (int v = _init; v < _end; v++)
    {
        Vertex *pv = get_vertex(v);
	int ii = (int)floor((pv->p[0]-min[0])/dl);
	int jj = (int)floor((pv->p[1]-min[1])/dl);
	
	for (int j = -point_size; j <= point_size; j++)
        {
            for (int i = -point_size; i <= point_size; i++)
            {
	        if (ii+i < 0 || ii+i >= nx || jj+j < 0 || jj+j >= ny )
		  continue;
                Pixel *pix = get_pixel(ii+i, jj+j);
                pix->set_flag(VERTEX, 1);
            }
        }
    }
}

void VoronoiDiagramBitmap::add_isolines(double dist_inc, double linewidth)
{
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);

            double p[2];
            get_coord(i, j, p);
            Vertex *pv = get_vertex(pix->_closest);
            double d = distance(pv->p, pv->a, p);
            if (fmod(d, dist_inc) >= 0.0 && fmod(d, dist_inc) <= linewidth)
                pix->set_flag(ISOLINE, 1);
        }
    }
}

void VoronoiDiagramBitmap::add_vorface()
{
    int b[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}};
    
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
	    Pixel *pix = get_pixel(i, j);
	    int cl = pix->_closest;
            for (int bb = 0; bb < 8; bb++)
            {
	        if (i+b[bb][0] < 0 || i+b[bb][0] >= nx || j+b[bb][1] < 0 || j+b[bb][1] >= ny )
		  continue;
                Pixel *pixtmp = get_pixel(i+b[bb][0], j+b[bb][1]);
		if (pixtmp->_closest != cl)
		{
                    pix->set_flag(VORFACE, 1);
		    pixtmp->set_flag(VORFACE, 1);
		}
            }
        }
    }
}

void VoronoiDiagramBitmap::lloyd()
{
    for (int v = _init; v < _end; v++)
    {
        Vertex *pv = get_vertex(v);
	double p[2];
	double bary[2] = {0.0, 0.0};
	int nb = 0;
	
        for (int j = 0; j < ny; j++)
        {
            for (int i = 0; i < nx; i++)
            {
		Pixel *pix = get_pixel(i, j);
		if (pix->_closest == v)
		{
		  get_coord(i, j, p);
		  bary[0] += p[0];
		  bary[1] += p[1];
		  nb++;
		}
            }
        }
        
        bary[0] /= (double)nb;
        bary[1] /= (double)nb;
	
	pv->p[0] = bary[0];
	pv->p[1] = bary[1];
	pv->a = _bg->get_angle(pv->p);
    }
}

double VoronoiDiagramBitmap::distance(double *ref, double angle, double *p)
{
    double ref1[2];
    double p1[2];

    double x = ref[0];
    double y = ref[1];
    ref1[0] = x*cos(angle) + y*sin(angle);
    ref1[1] = -x*sin(angle) + y*cos(angle);

    x = p[0];
    y = p[1];
    p1[0] = x*cos(angle) + y*sin(angle);
    p1[1] = -x*sin(angle) + y*cos(angle);
	
    return std::max(fabs(ref1[0]-p1[0]), fabs(ref1[1]-p1[1])); 
//     double pp = 8.0;
//     return pow( pow(ref1[0]-p1[0], pp) + pow(ref1[1]-p1[1], pp) ,  1.0/pp);
    return sqrt((ref1[0]-p1[0])*(ref1[0]-p1[0]) + (ref1[1]-p1[1])*(ref1[1]-p1[1]));
}

// double VoronoiDiagramBitmap::distance_dx(double *ref, double angle, double *p, double *dx)
// {
//     double ref1[2];
//     double p1[2];
// 
//     double x = ref[0];
//     double y = ref[1];
//     ref1[0] = x*cos(angle) + y*sin(angle);
//     ref1[1] = -x*sin(angle) + y*cos(angle);
// 
//     x = p[0];
//     y = p[1];
//     p1[0] = x*cos(angle) + y*sin(angle);
//     p1[1] = -x*sin(angle) + y*cos(angle);
//     
//     if (fabs(p1[0]-ref1[0]) > fabs(p1[1]-ref1[1]))
//     {
//       dx = p1[0]-ref1[0];
//       y = 0.0;
//     }
//     else
//     {
//       x = 0.0;
//       y = p1[1]-ref1[1];  
//     }
//     dir[0] = x*cos(angle) - y*sin(angle);
//     dir[1] = x*sin(angle) + y*cos(angle);  
//     double norm = sqrt(dir[0]*dir[0] + dir[1]*dir[1]);
//     dir[0] /= norm;
//     dir[1] /= norm;
// 	
//     return std::max(fabs(ref1[0]-p1[0]), fabs(ref1[1]-p1[1])); 
// //     double pp = 8.0;
// //     return pow( pow(ref1[0]-p1[0], pp) + pow(ref1[1]-p1[1], pp) ,  1.0/pp);
//     return sqrt((ref1[0]-p1[0])*(ref1[0]-p1[0]) + (ref1[1]-p1[1])*(ref1[1]-p1[1]));
// }

// void VoronoiDiagramBitmap::descent(double *ref, double angle, double *p, double *dir)
// {
//     double ref1[2];
//     double p1[2];
// 
//     double x = ref[0];
//     double y = ref[1];
//     ref1[0] = x*cos(angle) + y*sin(angle);
//     ref1[1] = -x*sin(angle) + y*cos(angle);
// 
//     x = p[0];
//     y = p[1];
//     
//     p1[0] = x*cos(angle) + y*sin(angle);
//     p1[1] = -x*sin(angle) + y*cos(angle);
//     
//     if (fabs(p1[0]-ref1[0]) > fabs(p1[1]-ref1[1]))
//     {
//       x = p1[0]-ref1[0];
//       y = 0.0;
//     }
//     else
//     {
//       x = 0.0;
//       y = p1[1]-ref1[1];  
//     }
//     dir[0] = x*cos(angle) - y*sin(angle);
//     dir[1] = x*sin(angle) + y*cos(angle);    
//     double norm = sqrt(dir[0]*dir[0] + dir[1]*dir[1]);
//     dir[0] /= norm;
//     dir[1] /= norm;
// }

void VoronoiDiagramBitmap::export_vor_ppma()
{
    static int count = 0;
    char filename[100];
    
    int vertex_RGB[3] = {0, 0, 0};
    int vorface_RGB[3] = {50, 50, 50};
    int isoline_RGB[3] = {100, 100, 100};

        
    sprintf(filename, "voronoi_bitmap_%d.ppma", count++);
    printf(" write file --> %s\n", filename);

    FILE *fp_rw = fopen (filename, "w");
    
    fprintf(fp_rw, "P3\n");
    fprintf(fp_rw, "# %s\n", filename);
    fprintf(fp_rw, "%d %d\n", nx, ny);
    fprintf(fp_rw, "%d\n", 255);
    
//     double dc = 360.0/(double)_range_max;
    double dc = 255.0/(double)_range_max;
    
    int row = 0;
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);
#if 1
            if (pix->get_flag(VERTEX))     
                fprintf(fp_rw, " %d %d %d", vertex_RGB[0], vertex_RGB[1], vertex_RGB[2]);
	    else if (pix->get_flag(VORFACE))  
		fprintf(fp_rw, " %d %d %d", vorface_RGB[0], vorface_RGB[1], vorface_RGB[2]);
	    else if (pix->get_flag(ISOLINE))  
		fprintf(fp_rw, " %d %d %d", isoline_RGB[0], isoline_RGB[1], isoline_RGB[2]);
            else
            {
                int rgb[3];
                HSVtoRGB((int)(pix->_closest*dc), 255, 255, &rgb[0], &rgb[1], &rgb[2]);
                fprintf(fp_rw, " %d %d %d", rgb[0], rgb[1], rgb[2]);
            }
#else
	    fprintf(fp_rw, " %d %d %d", (int)(pix->_closest*dc), (int)(pix->_closest*dc), (int)(pix->_closest*dc));
#endif
            row++;
            if (row >= 4)
            {
                fprintf(fp_rw, "\n");
                row = 0;
            }
        }
    }
}

void VoronoiDiagramBitmap::export_function_ppma()
{
    static int count = 0;
    char filename[100];
    
    int vertex_RGB[3] = {0, 0, 0};
    int vorface_RGB[3] = {50, 50, 50};
    int isoline_RGB[3] = {100, 100, 100};

    sprintf(filename, "voronoi_function_%d.ppma", count++);
    printf(" write file --> %s\n", filename);

    FILE *fp_rw = fopen (filename, "w");
    
    fprintf(fp_rw, "P3\n");
    fprintf(fp_rw, "# %s\n", filename);
    fprintf(fp_rw, "%d %d\n", nx, ny);
    fprintf(fp_rw, "%d\n", 255);
    
    double dc = 360.0/(double)_range_max;
//     double dc = 255.0/(double)_range_max;
    
    int row = 0;
    for (int j = 0; j < ny; j++)
    {
        for (int i = 0; i < nx; i++)
        {
            Pixel *pix = get_pixel(i, j);
#if 1
            if (pix->get_flag(VERTEX))     
                fprintf(fp_rw, " %d %d %d", vertex_RGB[0], vertex_RGB[1], vertex_RGB[2]);
	    else if (pix->get_flag(VORFACE))  
		fprintf(fp_rw, " %d %d %d", vorface_RGB[0], vorface_RGB[1], vorface_RGB[2]);
	    else if (pix->get_flag(ISOLINE))  
		fprintf(fp_rw, " %d %d %d", isoline_RGB[0], isoline_RGB[1], isoline_RGB[2]);
            else
            {
                int rgb[3];
                HSVtoRGB((int)(pix->_closest*dc), 255, 255, &rgb[0], &rgb[1], &rgb[2]);
                fprintf(fp_rw, " %d %d %d", rgb[0], rgb[1], rgb[2]);
            }
#else
	    fprintf(fp_rw, " %d %d %d", (int)(pix->_closest*dc), (int)(pix->_closest*dc), (int)(pix->_closest*dc));
#endif
            row++;
            if (row >= 4)
            {
                fprintf(fp_rw, "\n");
                row = 0;
            }
        }
    }
}