/* $Id: glstuff.c,v 1.12 2003/03/22 01:46:37 d3august Exp $
*/
/*  xtraceroute - graphically show traceroute information.
 *  Copyright (C) 1996-1998  Björn Augustsson 
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <stdlib.h>
#include <gtkgl/gtkglarea.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>     /* for cos(), sin(), and sqrt() */
#include "xt.h"

extern void sphere(int level);

extern GdkPixbuf *earth_texture;
extern GdkPixbuf *night_texture;
extern GdkPixbuf *created_texture;

//extern struct traceroute_stuff;  // from extprog.c
extern GLfloat zoom;

static GLUquadricObj *lineobj;
static GLUquadricObj *earthobj;
static GLUquadricObj *sitemarker;

static const GLfloat hiaccColor[]  = {0.1, 1.0,  0.1, 1.0}; // Green for good.
static const GLfloat medaccColor[] = {1.0, 1.0,  0.1, 1.0}; // Yellow for OK.
static const GLfloat loaccColor[]  = {1.0, 0.53, 0.0, 1.0}; // Orange for bad
static const GLfloat noaccColor[]  = {1.0, 0.1,  0.1, 1.0}; // Red for no info

static const GLfloat lineColor[]         = {1.0, 1.0, 0.1, 1.0}; // Yellow
static const GLfloat defaultColor[]      = {0.8, 0.8, 0.8, 1.0};

static const GLfloat selectedEmission[]  = {0.6, 0.6, 0.6, 1.0};
static const GLfloat defaultEmission[]   = {0.0, 0.0, 0.0, 1.0};

static const GLfloat lightZeroPosition[] = { 10.0,  4.0, 10.0, 1.0};
static const GLfloat lightZeroColor[]    = {  0.8,  1.0,  0.8, 1.0}; /* green-tinted */
static const GLfloat lightOnePosition[]  = { -1.0, -2.0,  1.0, 0.0};
static const GLfloat lightOneColor[]     = {  0.9,  0.9,  0.9, 1.0}; /* bright white */

static int render_mode = NORMAL_MODE;

/**
 * Set the Level Of Detail on the spheres generated in the future.
 * Only affects the earth-sphere, not the ones that represent
 * sites.
 */

void
set_sphere_lod(const int lod)
{
  user_settings->LOD = lod;
}

/**
 * Set the mode in which the earth is to be rendered. Valid modes are:
 * NORMAL_MODE (default, does nothing special)
 * SELECT_MODE (uses glLoadName so we can do picking.)
 */

void 
set_render_mode(int mode)
{
  render_mode = mode;
}

/**
 * between puts the coordinates for a point between the two
 * specified ones in (rlat, rlon).
 */

static void 
between(const GLdouble lat1, const GLdouble lon1,
	const GLdouble lat2, const GLdouble lon2,
	GLdouble *rlat, GLdouble *rlon)
  { 
   GLdouble x1 = tox(lat1, lon1);
   GLdouble y1 = toy(lat1, lon1);
   GLdouble z1 = toz(lat1, lon1); 
   GLdouble x2 = tox(lat2, lon2);
   GLdouble y2 = toy(lat2, lon2);
   GLdouble z2 = toz(lat2, lon2); 

   GLdouble  x = (x1 + x2) / 2.0;
   GLdouble  y = (y1 + y2) / 2.0;
   GLdouble  z = (z1 + z2) / 2.0;

/*  'Normalize' to the radius of the earth. (Which is always 1, since this
     is the Unit Earth (TM))  */

   GLdouble f = 1.0 / sqrt(x*x + y*y + z*z);
    x *= f;
    y *= f;
    z *= f;
    *rlat = tolat(x,y,z);
    *rlon = tolon(x,y,z);
  }

/**
 * This is only used from makeearth, to plot a line on the globe.
 * There is some difficultish maths in this one.
 *
 * "to" and "from" in the comments in the code refers to the two
 * points given as arguments.
 */

static void
plot_line(const GLfloat fromlat, const GLfloat fromlon, const GLfloat tolat, const GLfloat tolon)
{
  GLfloat vink;
  GLfloat linesweep;
  
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_DIFFUSE, lineColor);
  
  /*    Angle is arccos(Ax*Bx + Ay*By + Az*Bz)    */
  
  linesweep = acos(tox(tolat, tolon)*tox(fromlat, fromlon)
		   +toy(tolat, tolon)*toy(fromlat, fromlon)
		   +toz(tolat, tolon)*toz(fromlat, fromlon))
    * todeg;
  
  /*   This mess calculates the angle to rotate "to" to a position
       'south' of "from". It's a somewhat adapted version of a formula
       that calculates an angle in a triangle on a sphere given all three
       sides. (Found the original in Beta.)                           */
  
  vink = todeg*acos((sin(tolat*torad)-sin(fromlat*torad)*cos(linesweep*torad))/
		    ( cos(fromlat*torad) * sin(linesweep*torad) )      );
  
  /*   Compensate for the usual arccos problem. (It checks if "to" is
       'to the right' or 'to the left' of "from")                 */
  
  if(fromlon > 0.0)
    {
      if(tolon > fromlon || tolon < fromlon - 180.0)
	vink = -vink;
    }
  else
    {
      if(tolon > fromlon && tolon < fromlon + 180.0)
	vink = -vink;
    }
  
  /*   Rotate "to" to a position 'south' of "from".   */
  
  glRotatef(vink, tox(fromlat, fromlon),
	    toy(fromlat, fromlon),
	    toz(fromlat, fromlon));
  
  /*   ...and rotate them onto the z=0 plane.   */
  
  glRotatef(90.0+fromlon, 0.0, 1.0, 0.0);
  
  /*   Fix incorrect names on the lines, identifying them as the 
       sitemarker we just drew.             */
  
  if(render_mode == SELECT_MODE)
    glLoadName(NOT_SELECTABLE);
  
  partialtorus((GLfloat)fromlat-180.0, (GLfloat)linesweep, zoom);
  glPopMatrix();
}

/**
 * This is only used from makeearth, to plot a satellite above the 
 * globe.
 * (It's just plot_line, for high-latency links)
 *
 * "dest" and "from" in the comments in the code refers to the two
 * points given as arguments.
 */

static void
plot_sat(const GLfloat fromlat, const GLfloat fromlon, 
	 const GLfloat destlat, const GLfloat destlon)
{
  static const GLfloat satPanelColor[]  = { 0.0, 0.0, 0.2, 1.0}; /* Glossy dark-blue */
  static const GLfloat satBodyColor[]   = {1.0, 0.53, 0.0, 1.0}; /* Goldish */
  static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
  static const GLfloat mat_nospecular[] = { 0.0, 0.0, 0.0, 1.0 };

  GLfloat factor;
  GLfloat line_size = (SITE_MARKER_SIZE/2.0)/zoom;

  GLfloat fromx = tox(fromlat, fromlon);
  GLfloat fromy = toy(fromlat, fromlon);
  GLfloat fromz = toz(fromlat, fromlon);
  
  GLfloat destx = tox(destlat, destlon);
  GLfloat desty = toy(destlat, destlon);
  GLfloat destz = toz(destlat, destlon);
  
  GLfloat satx;
  GLfloat saty;
  GLfloat satz;
  GLdouble satlat, satlon;

  between(fromlat, fromlon,
	  destlat, destlon,
	  &satlat, &satlon);

  satx = tox(satlat, satlon);

#ifdef SAT_OVER_EQUATOR
  saty = toy(0.0, satlon);
#else
  saty = toy(satlat, satlon);
#endif

  satz = toz(satlat, satlon);
  
  /* Draw the satellite */
  
  glPushMatrix();
  
  factor = (GEOSYNC_SAT_ALT+PHYSICAL_EARTH_RADIUS) / PHYSICAL_EARTH_RADIUS;
  glTranslated(satx*factor,
	       saty*factor,
	       satz*factor);
  
  glMaterialfv(GL_FRONT, GL_DIFFUSE, satBodyColor);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 80);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
  
  gluSphere(sitemarker,SITE_MARKER_SIZE/zoom,7,7);
  
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, satPanelColor);
  glDisable(GL_CULL_FACE);
  glBegin(GL_QUADS);
  {
    const GLfloat panel_length = 15*SITE_MARKER_SIZE/zoom; 
    const GLfloat panel_width  = SITE_MARKER_SIZE/zoom; 
    
    glVertex3f( -panel_width,  -panel_width,  panel_width );
    glVertex3f( -panel_length, -panel_width,  panel_width );
    glVertex3f( -panel_length,  panel_width, -panel_width );
    glVertex3f( -panel_width,   panel_width, -panel_width );
    
    glVertex3f(  panel_length, -panel_width,  panel_width );
    glVertex3f(  panel_width,  -panel_width,  panel_width );
    glVertex3f(  panel_width,   panel_width, -panel_width );
    glVertex3f(  panel_length,  panel_width, -panel_width );
  }
  glEnd(); // GL_QUADS
  glEnable(GL_CULL_FACE);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_nospecular);
  
  glPopMatrix();
  
  /* Make the line from -> sat */
  
  glMaterialfv(GL_FRONT, GL_DIFFUSE, lineColor);
  glPushMatrix();
  {
    GLfloat diffx, diffy, diffz, difflen;
    
    glTranslatef(fromx, fromy, fromz);
    
    /* rotate around (0,0,1)(satx-fromx, saty-fromy, satz-fromz) */
    /* angle between a and b is acos(a*b / abs(a)*abs(b)) */
    
    diffx   = satx*factor - fromx;
    diffy   = saty*factor - fromy;
    diffz   = satz*factor - fromz;
    difflen = sqrt(diffx*diffx + diffy*diffy + diffz*diffz);
    
    //      printf("x: %2.3f\ty: %2.3f\tz: %2.3f\tlen: %2.3f\n", diffx, diffy, diffz, difflen);      
    
    // FIXME Undoubtedly there's an error here, acos only returns angles 
    //       between 0 and pi, so I better do some thinking.
    glRotatef(todeg*acos(diffz/difflen),
	      -diffy, diffx, 0.0);
    
    gluCylinder(lineobj,
		line_size,
		line_size,
		difflen,
		4,
		10);    // FIXME tweak this value later.
  }
  glPopMatrix();
  
  /* Make the line dest -> sat */
  
  glPushMatrix();
  {
    GLfloat diffx, diffy, diffz, difflen;
    
    glTranslated(destx, desty, destz);
    
    diffx   = satx*factor - destx;
    diffy   = saty*factor - desty;
    diffz   = satz*factor - destz;
    difflen = sqrt(diffx*diffx + diffy*diffy + diffz*diffz);
    
    glRotatef(todeg*acos(diffz/difflen),
	      -diffy, diffx, 0.0);
    
    gluCylinder(lineobj,
		line_size,
		line_size,
		difflen,
		4,
		10);    // FIXME tweak this value later.
  }      
  glPopMatrix();
  
}

/**
 * This is only used from makeearth, to plot a site on the globe.
 */
static void
plot_site(const int site_nr, const GLfloat lat, const GLfloat lon, 
	  const int height, const int selected, const int accuracy)
{
  GLfloat factor;

  glPushMatrix();
  if(selected)
    glMaterialfv(GL_FRONT, GL_EMISSION, selectedEmission);
  switch(accuracy)
    {
    case ACC_RFC_1876:
    case ACC_RFC_1712:
      glMaterialfv(GL_FRONT, GL_DIFFUSE, hiaccColor);
      break;
    case ACC_NDG_HOST:
    case ACC_NDG_NET:
    case ACC_LOCAL_SITE:
    case ACC_LOCAL_USER:
    case ACC_INCLUDED:
      glMaterialfv(GL_FRONT, GL_DIFFUSE, medaccColor);	    
      break;
    case ACC_SUFFIX:
      glMaterialfv(GL_FRONT, GL_DIFFUSE, loaccColor);
      break;
    case ACC_NONE:
      glMaterialfv(GL_FRONT, GL_DIFFUSE, noaccColor);
      break;
    default:
      printf("Argh! Unknown accuracy!\n");
      break;
    }
  factor = 1.0+(2.0*SITE_MARKER_SIZE*(height))/zoom;
  glTranslated(tox(lat, lon)*factor,
	       toy(lat, lon)*factor,
	       toz(lat, lon)*factor);
  
  if(render_mode == SELECT_MODE)
    glLoadName(site_nr);
  
  gluSphere(sitemarker,SITE_MARKER_SIZE/zoom,5,5);
  
  if (selected)
    glMaterialfv(GL_FRONT, GL_EMISSION, defaultEmission);
  
  glPopMatrix();
}

/**
 * map_texture() : map a texture to earthobj
 */

void 
map_texture(const GdkPixbuf* tex) 
{
  GLenum appropriate_format;

  if(gdk_pixbuf_get_has_alpha(tex))
    appropriate_format = GL_RGBA;
  else
    appropriate_format = GL_RGB;


  gluQuadricTexture(earthobj,GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  
  /* I would be clever and use the nice appropriate_format variable
     here, but that doesn't work, because gluBuild2DMipmaps doesn't
     support that. See above for rant on that. */
  gluBuild2DMipmaps(GL_TEXTURE_2D,
		    GL_RGBA,
		    gdk_pixbuf_get_width(tex),
		    gdk_pixbuf_get_height(tex),
		    appropriate_format,
		    GL_UNSIGNED_BYTE,
		    gdk_pixbuf_get_pixels(tex));
}


/*-------------------------------------------------------------------------*/
/* set_transparency()                                                      */
/*-------------------------------------------------------------------------*/
void 
set_transparency(gint blend_factor) 
{
  if (blend_factor) {
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    switch (blend_factor)  
      {
      case 1 : glBlendFunc(GL_ONE,GL_SRC_ALPHA); 
	break;
      }
    
  }
  else {
    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
  }
}

/*-------------------------------------------------------------------------*/
/* set_zoom() : only called from choose_zoom() ;                           */
/* modify the global variable zoom used as scaling factor for each redraw  */
/* of the world and modifies the point we look at iwhen zooming in/out.    */
/* When zoom in tends to max, the look at point tends to the upper pole.   */
/* When zoom tends to min, the look at point tends to earth center.        */
/*-------------------------------------------------------------------------*/
void set_zoom( gint zoom_type) {
  
  switch (zoom_type) 
    {
    case 0: 
      zoom = zoom + .2; 
      break;   /* zoom in */
    case 1: 
      zoom = zoom - .2; 
      break;   /* zoom out */
    case 2: 
      zoom = zoom * 2; 
      break;    /* zoom in x2 */
    case 3: 
      zoom = zoom / 2; 
      break;    /* zoom out x2*/
    case 4: 
      zoom = EARTH_SIZE ;
      break;  /* zoom default */
    }

  if (zoom > Z_OF_EYE - EARTH_SIZE)
    zoom=Z_OF_EYE - EARTH_SIZE;  /* prevents going inside the object */
  
  if (zoom <= EARTH_SIZE/4)
    zoom = EARTH_SIZE/4;              /* prevents going beyond "infinity" */
  
  //  glMatrixMode(GL_MODELVIEW);
  //  glPopMatrix();
  //  glLoadIdentity();
  /* look to y=0 for zoom min and make y tend to * EARTH_SIZE when zoom max*/
  
  //  gluLookAt( 0.0,                   0.0,                 Z_OF_EYE, 
  //	     0.0,   EARTH_SIZE-(EARTH_SIZE/(zoom*zoom)),   0.0,
  //	     0.0,                   1.0,                   0.0);
  
  //  glPushMatrix(); /* dummy push so we can pop on model recalc */
}


/*-------------------------------------------------------------------------*/
/* set_view_motion() : only called from mouse_motion() ; this is the GL    */
/* implementation (hence appart from main.c) of moving the point we look   */
/* at on an x-y axis.                                                      */
/*-------------------------------------------------------------------------*/
void set_view_motion(gfloat strafex, gfloat strafey) 
{
  glPopMatrix();
  glTranslatef((GLfloat) strafex, (GLfloat) strafey,0);
  glPushMatrix(); /* dummy push so we can pop on model recalc */
}


/**
 * Set up the OpenGL state, init variables etc.
 */

void 
init_gl(GtkWidget *wi, gpointer data)
{
  
  if (!gtk_gl_area_make_current(GTK_GL_AREA(wi)))
    { 
      printf("make_current failed early in init_gl()\n");
    }

  lineobj  = gluNewQuadric();
  earthobj = gluNewQuadric();
  sitemarker = gluNewQuadric();
  gluQuadricDrawStyle(lineobj, GLU_FILL);
//  gluQuadricOrientation(earthobj, GLU_INSIDE); // Wow, that got strange...
  
  
  glEnable(GL_NORMALIZE);  /* Or zoom'll break the lighting */
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective( /* field of view in degree */ 40.0,
		  /* aspect ratio            */  1.0,
		  /* Z near                  */  1.0, 
		  /* Z far                   */ 10.0);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0.0, 0.0, Z_OF_EYE,    /* eye is normally at (0,0,4) */
	    0.0, 0.0, 0.0,         /* center is at (0,0,0) */
	    0.0, 1.0, 0.0);        /* up is in postive Y direction */
  
  glPushMatrix(); /* dummy push so we can pop on model recalc */
  
  /* Lighting stuff. */
  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
  glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
  glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
  glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
  glLightfv(GL_LIGHT1, GL_POSITION, lightOnePosition);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, lightOneColor);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);
  
  glDisable(GL_BLEND);
  glEnable(GL_CULL_FACE);
  
#ifdef FLAT_SHADING
  glShadeModel(GL_FLAT);
#endif
  
  switch(user_settings->lighting_mode)
    {
    case DAY_AND_NIGHT:
      composite_texture(night_texture, earth_texture, &created_texture);
      map_texture(created_texture);
      break;
    case DAY_ONLY:
      map_texture(earth_texture);
      break;
    default:
      printf("Funky lighting mode doing textures: %d\n", 
	     user_settings->lighting_mode);
      exit(EXIT_FAILURE);
    }
  
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
#ifdef UGLY_LINES
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#else
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#endif //UGLY_LINES
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  gtk_gl_area_swapbuffers(GTK_GL_AREA(wi));
}

/**
 * This determines wether to plot a site on top of another or put it 
 * on the ground and connect it and the previous one with a line.
 */

static int far_enough_apart(const site a, const site b)
{
  float londiff = fabs(a.lon - b.lon);
  float latdiff = fabs(a.lat - b.lat);
  
  /* This test should really check true distance, but hey... */
  return (londiff > 3.0 || latdiff > 3.0);
}


/**
 * This one builds the WORLD displaylist. It gets called whenever there
 * is new information to be "plotted". (The function, not the list)
 *
 * This is the kind of code everyone hates. 300 lines, one bazillion
 * levels of indentation, obscure maths, and weird names for stuff.
 *
 * It's certainly scheduled for a rewrite soon, but until then...
 *
 * It's a little better now, as of 0.8.8. Not good, better.
 **/

void makeearth(void)
{
  int i;
  int sites_on_top = 0;  
  int bad_behind   = 0;
  int any_good_sites_plotted = 0;

  // glxmakecurrent isn't allowed in select or feedback modes.
  if (render_mode == NORMAL_MODE)
    {
      if (!gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
	printf("make_current failed early in makeearth()\n");
    }
  glNewList(WORLD, GL_COMPILE);
  glPushMatrix();
  
  glMaterialfv(GL_FRONT, GL_DIFFUSE, defaultColor);
  glEnable(GL_TEXTURE_2D);
  
#if 0
  // This doesn't work. For some reason the texture needs to be upside
  //  down for gluSphere to work. (It generates weird texture
  //  coordinates)
  gluSphere(earthobj, 1.0, sphere_lod, sphere_lod);
#else
  sphere(user_settings->LOD);
#endif //0
  
  glDisable(GL_TEXTURE_2D);
  
  glPopMatrix();
  
  for(i=0 ; i<MAX_SITES && sites[i].draw ; i++) /* Here we go */
    {
      /* If the first sites doesn't reply, stack them 
	 under the first known one */
      if(sites[i].accuracy == ACC_NONE && !any_good_sites_plotted)
	continue;
      
      if(!any_good_sites_plotted) /* "good" means accuracy > ACC_NONE */
	{
	  int ii;
	  for(ii=0 ; ii<i ; ii++)
	    {
	      plot_site(ii, sites[i].lat, 
			sites[i].lon,
			ii,
			sites[ii].selected,
			sites[ii].accuracy);
	    }
	  plot_site(i, sites[i].lat, sites[i].lon, ii, 
		    sites[i].selected, sites[i].accuracy);
	  any_good_sites_plotted = 1;
	  continue;
	}

      /* Normal case begins here */
      if(i > 0 && far_enough_apart(sites[i], sites[i-1]))
	{
	  plot_site(i, sites[i].lat, sites[i].lon, 0, 
		    sites[i].selected, sites[i].accuracy);
	  
	  sites_on_top = 0;
	  
	  /* Make a line to it from the last one. (Or a satellite) 

             The "260" value is because 240.2 milliseconds is the time
             it takes light to travel from the ground to 36000 km (where
             geosync satellites are), and back. Seems reasonable. */

	  if(sites[i].time - sites[i-1].time < 260) 
	    {
	      plot_line(sites[i-1].lat, sites[i-1].lon,
			sites[ i ].lat, sites[ i ].lon);
	    }
	  else
	    {
	      plot_sat(sites[i-1].lat, sites[i-1].lon,
			sites[ i ].lat, sites[ i ].lon);
	    }
	  if(bad_behind) /* Then stack them on the line. */
	    {
	      int ii;
	      GLdouble lat, lon;
	      between(sites[ i ].lat,sites[ i ].lon,
		      sites[i-1].lat,sites[i-1].lon,
		      &lat, &lon);
	      for(ii=0;ii<bad_behind;ii++)
		{
		  plot_site(i-bad_behind+ii, lat, lon, ii+1,
			    sites[i-bad_behind+ii].selected, ACC_NONE);
		}
	      bad_behind = 0;
	    }
	}
      else   /* This site and the one before it is very close */ 
	{
	  if(sites[i].accuracy == ACC_NONE)
	    bad_behind++;
	  else
	    {
	      if(bad_behind)
		{
		  int ii;
		  for(ii=0 ; ii<bad_behind ; ii++)
		    {
		      plot_site(i-bad_behind+ii, 
				sites[i].lat, 
				sites[i].lon, 
				1+sites_on_top++,
				sites[i-bad_behind+ii].selected,
				ACC_NONE);
		    }
		  bad_behind = 0;
		}
	      plot_site(i, sites[i].lat, sites[i].lon, 1+sites_on_top,
			sites[i].selected, sites[i].accuracy);
	      sites_on_top++;
	    }
	}
    }
  
  /* If there are still bad_behind, plot them on   */
  /* top of the last known site.                   */
  
  /* !draw , or MAX_SITES sites. */
  
  if(bad_behind)
    {
      int ii;

      for(ii=0 ; ii<bad_behind ; ii++)
	{
	  plot_site(i-bad_behind+ii,
		    sites[i-1].lat,
		    sites[i-1].lon,
		    1+sites_on_top++, 
		    sites[i-bad_behind+ii].selected,
		    ACC_NONE);
	}
      bad_behind = 0;
    }
  glEndList();

  if(render_mode != SELECT_MODE)
    redraw(GTK_WIDGET(glarea), NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1