/*  misc.c */


/*
 * Vis5D system for visualizing five dimensional gridded data sets.
 * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
 * Dave Santek, and Andre Battaiola.
 *
 * 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.
 *
 * As a special exception to the terms of the GNU General Public
 * License, you are permitted to link Vis5D with (and distribute the
 * resulting source and executables) the LUI library (copyright by
 * Stellar Computer Inc. and licensed for distribution with Vis5D),
 * the McIDAS library, and/or the NetCDF library, where those
 * libraries are governed by the terms of their own licenses.
 *
 * 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 "../config.h"


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "api.h"
#include "globals.h"
#include "memory.h"
#include "imemory.h"
#include "misc.h"
#include "proj.h"
#include "sync.h"
#include "vis5d.h"


/*
 * Print an error message and abort.
 */
void die( char *msg )
{
   printf("Error: %s\n",msg);
   abort();
}




/*
 * C implementation of unix 'which' command:  find the full path to
 * where the specified executable resides.
 * Input:  file - executable file to search for
 * Output:  fullpath - the full path/name for the file
 * Return:  1 = ok, 0 = file not found
 */
int which( char *file, char *fullpath )
{
   char *pathvar, *pos;
   char path[1000];
   int len;
   char s[1000];
   struct stat buf;

   /* get the value of PATH from the user's environment */
   pathvar = getenv( "PATH" );

   /* try each path */
   len = 0;
   for (pos=pathvar;*pos;pos++) {
      if (*pos==':') {

         path[len] = 0;
         /* try it */
         strcpy( s, path );
         strcat( s, "/" );
         strcat( s, file );

         if (stat(s, &buf) == 0) {
            /* found */
            if ((buf.st_mode & S_IEXEC)) {
               /* and executable */
               strcpy( fullpath, s );
               return 1;
            }
         }
         /* go on to next path */
         len = 0;
      }
      else {
         path[len++] = *pos;
      }
   }

   /* if we get here, we didn't find visad-d, try the current directory */
   strcpy( s, "./" );
   strcat( s, file );

   if (stat(s, &buf)==0 && (buf.st_mode & S_IEXEC)) {
      /* found it! */
      strcpy( fullpath, s );
      return 1;
   }

   return 0;
}



/*
 * Check if the named program is installed on the system.
 * Return:  1 = installed, 0 = not installed.
 */
int installed( char *program )
{
   char path[1000];

   if (which(program,path)==0) {
/*
      printf("Can't save/print window image because '%s' not found.\n",
              program);
      printf("Install it from your OS CD/tape.\n");
*/
      return 0;
   }
   else {
      return 1;
   }
}



/*** round ************************************************************
   Round off x to a 'nice' value.
**********************************************************************/
float round( float x )
{
   float base, fudge;
   int temp;

   for (base=1000000.0; fabs(x/base)<1.0 && base>0.000001; base/=10.0);

   fudge = (x>0.0) ? 0.5 : -0.5;
   temp = (int) (x/base + fudge);
   return temp * base;
}

/* Input: index - the index of the vis5d_ctx
 * Output: it will return the position in the Display_Context's
 *         ctxarray of the desired vis5d_ctx index
 */
int return_ctx_index_pos( Display_Context dtx, int index)
{
   int yo, spandex;
   for (yo = 0; yo < dtx->numofctxs; yo++){
      spandex = dtx->ctxarray[yo];
      if (spandex == index){
         return yo;
      }
   }
   /*printf("error in return_ctx_index_pos\n"); */
   return -1;
}

/* Input: index - the index of the vis5d_ctx
 *        time - the timestep of the display_ctx
 * Output: return the timestep associates with the display_ctx's
 *         timestep
 */
int return_ctx_time( Display_Context dtx, int index, int time)
{
   int yo;
   for (yo = 0; yo < dtx->numofctxs; yo++){
      if (index == dtx->TimeStep[time].owners[yo]){
         return dtx->TimeStep[time].ownerstimestep[yo];
      }
   }
   printf("error in return_ctx_time\n");
   return -1;
}

/*
 * When a horizontal slice is moved to a new position (in grid coords),
 * this function should be called to update the position in Z and height.
 * Input:  level - position of slice in grid levels
 *         z - position of slice in Z graphics coords
 *         hgt - position of slice in height coords.
 */
void new_hslice_pos( Context ctx, float level, float *z, float *hgt )
{
   *hgt = gridlevelPRIME_to_height( ctx->dpy_ctx, level);
   *z = height_to_zPRIME(ctx->dpy_ctx, *hgt);
}

void new_hwindslice_pos( Display_Context dtx, float level, float *z, float *hgt )
{
   *z = gridlevelPRIME_to_zPRIME( dtx, -1, -1, level );
   *hgt =gridlevelPRIME_to_height( dtx, level );
}


/*
 * Similar function to new_hslice_pos().
 * NOTE: the graphics and geo coordinates are PRIME
 */
void new_vslice_pos( Context ctx, float r, float c,
                     float *x, float *y, float *lat, float *lon )
{
   float zero = 0.0, z;
   float row, col;

   row = r;  /* this is needed because r and c are really doubles */
   col = c;

   gridPRIME_to_xyzPRIME( ctx->dpy_ctx, -1, -1, 1, &row, &col, &zero, x, y, &z );
   rowcolPRIME_to_latlon( ctx->dpy_ctx, -1, -1, r, c, lat, lon );
}

void new_vwindslice_pos( Display_Context dtx, float r, float c,
                     float *x, float *y, float *lat, float *lon )
{
   float zero = 0.0, z;
   float row, col;

   row = r;  /* this is needed because r and c are really doubles */
   col = c;

   gridPRIME_to_xyzPRIME( dtx, -1, -1, 1, &row, &col, &zero, x, y, &z );
   rowcolPRIME_to_latlon( dtx, -1, -1, r, c, lat, lon );
}



/*
 * Set the initial position, etc. of the graphics for a specified variable.
 */
void init_graphics_pos( Context ctx, int var )
{
   Display_Context dtx;
   float l;

	/* TODO: This should be split into initialization functions for each graph type and variable */

   dtx = ctx->dpy_ctx;
   ctx->IsoLevel[var] = ctx->Variable[var]->MinVal;

   l =  ((float) dtx->LowLev + (float) (dtx->Nl-1) / 2);

	if(! ctx->Variable[var]->HSliceRequest){
	  ctx->Variable[var]->HSliceRequest = (hslice_request *) allocate(ctx,sizeof(hslice_request));
	}

	ctx->Variable[var]->HSliceRequest->stipple=VIS5D_SOLID_LINE;
   ctx->Variable[var]->HSliceRequest->linewidth = 1;
   ctx->Variable[var]->HSliceRequest->Level = l;
   new_hslice_pos( ctx, ctx->Variable[var]->HSliceRequest->Level, &ctx->Variable[var]->HSliceRequest->Z,
                   &ctx->Variable[var]->HSliceRequest->Hgt );
   if (ctx->Variable[var]->MinVal > ctx->Variable[var]->MaxVal) {
      ctx->Variable[var]->HSliceRequest->Interval = 0.0;
   }
   else {
      ctx->Variable[var]->HSliceRequest->Interval = round( (ctx->Variable[var]->MaxVal - ctx->Variable[var]->MinVal)
                                        / 10.0 );
   }

   ctx->Variable[var]->HSliceRequest->LowLimit = ctx->Variable[var]->MinVal;
   ctx->Variable[var]->HSliceRequest->HighLimit = ctx->Variable[var]->MaxVal;

	if(! ctx->Variable[var]->CHSliceRequest){
	  ctx->Variable[var]->CHSliceRequest = (hslice_request *) allocate(ctx,sizeof(hslice_request));
	  ctx->Variable[var]->CHSliceRequest->fillstyle = GL_FILL;
	  ctx->Variable[var]->CHSliceRequest->textureflag=0;
	}
   ctx->Variable[var]->CHSliceRequest->Level = l;
   ctx->Variable[var]->CHSliceRequest->LowLimit = ctx->Variable[var]->MinVal;
   ctx->Variable[var]->CHSliceRequest->HighLimit = ctx->Variable[var]->MaxVal;

   new_hslice_pos( ctx, ctx->Variable[var]->CHSliceRequest->Level,
                   &ctx->Variable[var]->CHSliceRequest->Z, &ctx->Variable[var]->CHSliceRequest->Hgt );

	if(! ctx->Variable[var]->VSliceRequest){
	  ctx->Variable[var]->VSliceRequest = (vslice_request *) allocate(ctx,sizeof(vslice_request));
#ifdef DEBUG_MEM	  
	  printf("VSliceRequest 0x%x %d\n",ctx->Variable[var]->VSliceRequest,sizeof(vslice_request));
#endif
	}
	ctx->Variable[var]->VSliceRequest->stipple=VIS5D_SOLID_LINE;
   ctx->Variable[var]->VSliceRequest->linewidth = 1;

   ctx->Variable[var]->VSliceRequest->R1 = (float) (dtx->Nr-1) / 2.0;
   ctx->Variable[var]->VSliceRequest->R2 = 0.0;
   ctx->Variable[var]->VSliceRequest->C1 = (float) (dtx->Nr-1) / 2.0;
   ctx->Variable[var]->VSliceRequest->C2 = (float) (dtx->Nc-1);
   if (ctx->Variable[var]->MinVal > ctx->Variable[var]->MaxVal) {
      ctx->Variable[var]->VSliceRequest->Interval = 0.0;
   }
   else {
      ctx->Variable[var]->VSliceRequest->Interval = round( (ctx->Variable[var]->MaxVal - ctx->Variable[var]->MinVal)
                                        / 10.0 );
   }

   ctx->Variable[var]->VSliceRequest->LowLimit = ctx->Variable[var]->MinVal;
   ctx->Variable[var]->VSliceRequest->HighLimit = ctx->Variable[var]->MaxVal;

   new_vslice_pos( ctx, ctx->Variable[var]->VSliceRequest->R1, ctx->Variable[var]->VSliceRequest->R2,
                   &ctx->Variable[var]->VSliceRequest->X1, &ctx->Variable[var]->VSliceRequest->Y1,
                   &ctx->Variable[var]->VSliceRequest->Lat1, &ctx->Variable[var]->VSliceRequest->Lon1 );
   new_vslice_pos( ctx, ctx->Variable[var]->VSliceRequest->C1, ctx->Variable[var]->VSliceRequest->C2,
                   &ctx->Variable[var]->VSliceRequest->X2, &ctx->Variable[var]->VSliceRequest->Y2,
                   &ctx->Variable[var]->VSliceRequest->Lat2, &ctx->Variable[var]->VSliceRequest->Lon2 );

	if(! ctx->Variable[var]->CVSliceRequest){
	  ctx->Variable[var]->CVSliceRequest = (vslice_request *) allocate(ctx,sizeof(vslice_request));
	  ctx->Variable[var]->CVSliceRequest->fillstyle = GL_FILL;
	  ctx->Variable[var]->CVSliceRequest->textureflag=0;
	}

   ctx->Variable[var]->CVSliceRequest->R1 = ctx->Variable[var]->VSliceRequest->R1;
   ctx->Variable[var]->CVSliceRequest->R2 = ctx->Variable[var]->VSliceRequest->R2; 
   ctx->Variable[var]->CVSliceRequest->C1 = ctx->Variable[var]->VSliceRequest->C1;
   ctx->Variable[var]->CVSliceRequest->C2 = ctx->Variable[var]->VSliceRequest->C2;
   ctx->Variable[var]->CVSliceRequest->LowLimit = ctx->Variable[var]->MinVal;
   ctx->Variable[var]->CVSliceRequest->HighLimit = ctx->Variable[var]->MaxVal;

   new_vslice_pos( ctx, ctx->Variable[var]->CVSliceRequest->R1, ctx->Variable[var]->CVSliceRequest->R2,
                   &ctx->Variable[var]->CVSliceRequest->X1, &ctx->Variable[var]->CVSliceRequest->Y1,
                   &ctx->Variable[var]->CVSliceRequest->Lat1, &ctx->Variable[var]->CVSliceRequest->Lon1 );
   new_vslice_pos( ctx, ctx->Variable[var]->CVSliceRequest->C1, ctx->Variable[var]->CVSliceRequest->C2,
                   &ctx->Variable[var]->CVSliceRequest->X2, &ctx->Variable[var]->CVSliceRequest->Y2,
                   &ctx->Variable[var]->CVSliceRequest->Lat2, &ctx->Variable[var]->CVSliceRequest->Lon2 );
}




/*
 * Free a traj struct and any memory it points to.
 */
static void free_traj( Display_Context dtx, struct traj *t )
{
   int bytes;
   Context trajuctx;

   trajuctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->TrajUowner)];

   /* WLH 4 Nov 98 */
   if (trajuctx == NULL) return;

   /* vertices */
   bytes = 3 * t->length * sizeof(int_2);
   deallocate( trajuctx, t->verts, bytes );

   if (t->kind==1) {
      /* ribbon normals */
      bytes = 3 * t->length * sizeof(int_1);
      deallocate( trajuctx, t->norms, bytes );
   }

   if (t->colors) {
      /* colors */
      bytes = t->length * sizeof(uint_1);
      deallocate( trajuctx, t->colors, bytes );
   }

   bytes = trajuctx->NumTimes * sizeof(uint_2);
   deallocate( trajuctx, t->start, bytes );
   deallocate( trajuctx, t->len, bytes );
   deallocate( trajuctx, t, sizeof(struct traj) );
}



/*** del_last_traj ****************************************************
   Delete the most recent trajectory.
**********************************************************************/
void del_last_traj( Display_Context dtx )
{
   LOCK_ON( TrajLock );

   if (dtx->NumTraj) {
      free_traj( dtx, dtx->TrajTable[dtx->NumTraj-1] );
      dtx->TrajTable[dtx->NumTraj-1] = NULL;
      dtx->NumTraj--;
   }

   LOCK_OFF( TrajLock );
}




/*** del_traj_group ***************************************************
   Delete a group of trajectories.
   Input:  g - the trajectory group number in [0..VIS5D_TRAJ_SETS-1].
**********************************************************************/
void del_traj_group( Display_Context dtx, int g )
{
   int i, j;

   LOCK_ON( TrajLock );

   i = 0;
   while (i<dtx->NumTraj) {
      if (dtx->TrajTable[i]->group==g) {
         if (i<MAXTRAJ-1) {
            free_traj( dtx, dtx->TrajTable[i] );
            /* delete this one by shifting remaining trajectories backward */
            for (j=i+1;j<dtx->NumTraj;j++) {
               dtx->TrajTable[j-1] = dtx->TrajTable[j];
            }
         }
         dtx->NumTraj--;
      }
      else {
         i++;
      }
   }

   LOCK_OFF( TrajLock );
}




/*
 * Deallocate an isosurface's memory.
 */
int free_isosurface( Context ctx, int time, int var )
{
   Display_Context dtx;
   int ctime, ftime, t, total;
   
   dtx = ctx->dpy_ctx;
   total = 0;
   if (!ctx->SameIsoColorVarOwner[var]){
      ftime = dtx->TimeStep[time].ownerstimestep[return_ctx_index_pos(dtx,
                                  ctx->context_index)];
      for(t=0; t < dtx->NumTimes; t++){
         ctime = dtx->TimeStep[t].ownerstimestep[return_ctx_index_pos(dtx,
                                                 ctx->context_index)];
         if ( ctime==ftime){
            if (ctx->Variable[var]->SurfTable[time]->valid) {
               int b1, b2, b3, b4;
               /* vertices */
               b1 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(int_2) * 3;
               if (b1) {
                  deallocate( ctx, ctx->Variable[var]->SurfTable[time]->verts, b1 );
               }
               /* normals */
               b2 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(int_1) * 3;
               if (b2) {
                  deallocate( ctx, ctx->Variable[var]->SurfTable[time]->norms, b2 );
               }
               /* indices */
               b3 = ctx->Variable[var]->SurfTable[time]->numindex * sizeof(uint_index);
               if (b3) {
                  deallocate( ctx, ctx->Variable[var]->SurfTable[time]->index, b3 );
               }
               /* colors */
               if (ctx->Variable[var]->SurfTable[time]->colors) {
                  b4 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(uint_1);
                  deallocate( ctx, ctx->Variable[var]->SurfTable[time]->colors, b4 );
                  ctx->Variable[var]->SurfTable[time]->colors = NULL;
               }
               else {
                  b4 = 0;
               }
               ctx->Variable[var]->SurfTable[time]->valid = 0;
               total += b1 + b2 + b3 + b4;
            }
         }
      }
      return total;
   }
   
   else{

      if (ctx->Variable[var] && 
			 ctx->Variable[var]->SurfTable[time] && 
			 ctx->Variable[var]->SurfTable[time]->valid) {
         int b1, b2, b3, b4;
         /* vertices */
         b1 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(int_2) * 3;
         if (b1) {
            deallocate( ctx, ctx->Variable[var]->SurfTable[time]->verts, b1 );
         }
         /* normals */
         b2 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(int_1) * 3;
         if (b2) {
            deallocate( ctx, ctx->Variable[var]->SurfTable[time]->norms, b2 );
         }
         /* indices */
         b3 = ctx->Variable[var]->SurfTable[time]->numindex * sizeof(uint_index);
         if (b3) {
            deallocate( ctx, ctx->Variable[var]->SurfTable[time]->index, b3 );
         }
         /* colors */
         if (ctx->Variable[var]->SurfTable[time]->colors) {
            b4 = ctx->Variable[var]->SurfTable[time]->numverts * sizeof(uint_1);
            deallocate( ctx, ctx->Variable[var]->SurfTable[time]->colors, b4 );
            ctx->Variable[var]->SurfTable[time]->colors = NULL;
         }
         else {
            b4 = 0;
         }
         ctx->Variable[var]->SurfTable[time]->valid = 0;
         return b1 + b2 + b3 + b4;
      }
      else 
		  {
         return 0;
      }
   }
	return 0;
}



int free_textplot( Irregular_Context itx, int time)
{
   if (itx->TextPlotTable[time].valid){
      int b1, b2;
      
      b1 = itx->TextPlotTable[time].numverts * sizeof(int_2) * 3;
      if (b1){
         i_deallocate( itx, itx->TextPlotTable[time].verts, b1);
      }
      if (itx->TextPlotTable[time].colors){
         b2 = itx->TextPlotTable[time].numverts/2 * sizeof(uint_1);
         if (b2){
            i_deallocate( itx, itx->TextPlotTable[time].colors, b2);
         }
      }
      itx->TextPlotTable[time].valid = 0;
      return b1 + b2;
   }
   else{
      return 0;
   }
}
 

int free_hslice( Context ctx, int time, int var )
{
  int i;
  if(! (ctx->Variable[var] && ctx->Variable[var]->HSliceTable[time]))
	 return 0;
   if (ctx->Variable[var]->HSliceTable[time]->valid) {
      int b1, b2, b3, b4;

      b1 = ctx->Variable[var]->HSliceTable[time]->num1 * sizeof(int_2) * 3;
      if (b1) {
         deallocate( ctx, ctx->Variable[var]->HSliceTable[time]->verts1, b1 );
      }
      b2 = ctx->Variable[var]->HSliceTable[time]->num2 * sizeof(int_2) * 3;
      if (b2) {
         deallocate( ctx, ctx->Variable[var]->HSliceTable[time]->verts2, b2 );
      }
      b3 = ctx->Variable[var]->HSliceTable[time]->num3 * sizeof(int_2) * 3;
      if (b3) {
         deallocate( ctx, ctx->Variable[var]->HSliceTable[time]->verts3, b3 );
      }
      b4 = ctx->Variable[var]->HSliceTable[time]->numboxverts * 3 * sizeof(float);
      if (b4) {
         deallocate( ctx, ctx->Variable[var]->HSliceTable[time]->boxverts, b4 );
      }
      ctx->Variable[var]->HSliceTable[time]->valid = 0;

      return b1 + b2 + b3 + b4;
   }
   else 
	  {
		 return 0;
	  }

}




int free_vslice( Context ctx, int time, int var )
{
  int i;
  if(! (ctx->Variable[var] && ctx->Variable[var]->VSliceTable[time]))
	 return 0;


   if ( ctx->Variable[var]->VSliceTable[time]->valid) {
      int b1, b2, b3, b4;

      if (ctx->Variable[var]->VSliceTable[time]->valid
          && ctx->Variable[var]->VSliceTable[time]->num1) {
         b1 = ctx->Variable[var]->VSliceTable[time]->num1 * sizeof(int_2) * 3;
         deallocate( ctx, ctx->Variable[var]->VSliceTable[time]->verts1, b1 );
      }
      if (ctx->Variable[var]->VSliceTable[time]->valid
          && ctx->Variable[var]->VSliceTable[time]->num2) {
         b2 = ctx->Variable[var]->VSliceTable[time]->num2 * sizeof(int_2) * 3;
         deallocate( ctx, ctx->Variable[var]->VSliceTable[time]->verts2, b2 );
      }
      if (ctx->Variable[var]->VSliceTable[time]->valid
          && ctx->Variable[var]->VSliceTable[time]->num3) {
         b3 = ctx->Variable[var]->VSliceTable[time]->num3 * sizeof(int_2) * 3;
         deallocate( ctx, ctx->Variable[var]->VSliceTable[time]->verts3, b3 );
      }
      b4 = ctx->Variable[var]->VSliceTable[time]->numboxverts * 3 * sizeof(float);
      if (b4) {
         deallocate( ctx, ctx->Variable[var]->VSliceTable[time]->boxverts, b4 );
      }
      ctx->Variable[var]->VSliceTable[time]->valid = 0;
      return b1 + b2 + b3 + b4;
   }
   else 
	  {
		 return 0;
	  }
}



int free_chslice( Context ctx, int time, int var )
{
  if(! (ctx->Variable[var] && ctx->Variable[var]->CHSliceTable[time]))
	 return 0;
   if (ctx->Variable[var]->CHSliceTable[time]->valid) {
      int nrnc, b1, b2;
      nrnc = ctx->Variable[var]->CHSliceTable[time]->rows
           * ctx->Variable[var]->CHSliceTable[time]->columns;
      b1 = nrnc * sizeof(uint_1);
      deallocate( ctx, ctx->Variable[var]->CHSliceTable[time]->color_indexes, b1 );
      b2 = 3 * nrnc * sizeof(int_2);
      deallocate( ctx, ctx->Variable[var]->CHSliceTable[time]->verts, b2 );
      ctx->Variable[var]->CHSliceTable[time]->valid = 0;
      return b1 + b2;
   }
   else 
	  {
		 return 0;
	  }
}




int free_cvslice( Context ctx, int time, int var )
{
  if(! (ctx->Variable[var] && ctx->Variable[var]->CVSliceTable[time]))
	 return 0;
   if (ctx->Variable[var]->CVSliceTable[time]->valid) {
      int nrnc, b1, b2;
      nrnc = ctx->Variable[var]->CVSliceTable[time]->rows
           * ctx->Variable[var]->CVSliceTable[time]->columns;
      b1 = nrnc * sizeof(uint_1);
      deallocate( ctx, ctx->Variable[var]->CVSliceTable[time]->color_indexes, b1 );
      b2 = 3 * nrnc * sizeof(int_2);
      deallocate( ctx, ctx->Variable[var]->CVSliceTable[time]->verts, b2 );
      ctx->Variable[var]->CVSliceTable[time]->valid = 0;
      return b1 + b2;
   }
   else 
	  {
      return 0;
   }
}




/*
 * Free all the graphics of a specified physical variable.
 */
int free_param_graphics( Context ctx, int var )
{
   int time;

   for (time=0;time<ctx->NumTimes;time++) {
      LOCK_ON( GfxLock );
      /* isosurfaces */
      free_isosurface( ctx, time, var );

      /* horizontal contour slices */
      free_hslice( ctx, time, var );

      /* vertical contour slices */
      free_vslice( ctx, time, var );

      /* horizontal colored slices */
      free_chslice( ctx, time, var );

      /* vertical colored slices */
      free_cvslice( ctx, time, var );

      LOCK_OFF( GfxLock );
   }
   return 0;
}





int free_hwind( Display_Context dtx, int time, int ws )
{
   Context uctx;

   uctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->HWindTable[ws][time].valid) {
      int b1, b2;
/* MJK 10.14.98 
      dtx->DisplayHWind[ws] = 0;
*/
      b1 = dtx->HWindTable[ws][time].nvectors * sizeof(int_2) * 3;
/* WLH 4 Nov 98
      if (b1) {
*/
      /* WLH 4 Nov 98 */
      if (b1 && uctx) {
         deallocate( uctx, dtx->HWindTable[ws][time].verts, b1 );
      }
      b2 = dtx->HWindTable[ws][time].numboxverts * 3 * sizeof(float);
/* WLH 4 Nov 98
      if (b2) {
*/
      /* WLH 4 Nov 98 */
      if (b2 && uctx) {
         deallocate( uctx, dtx->HWindTable[ws][time].boxverts, b2 );
      }
      dtx->HWindTable[ws][time].valid = 0;
      return b1 + b2;
   }
   else {
      return 0;
   }
}



int free_vwind( Display_Context dtx, int time, int ws )
{
   Context uctx;

   uctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->VWindTable[ws][time].valid) {
      int b1, b2;
/* MJK 10.14.98       
      dtx->DisplayVWind[ws] = 0;
*/
      b1 = dtx->VWindTable[ws][time].nvectors * sizeof(int_2) * 3;
/* WLH 4 Nov 98
      if (b1) {
*/
      /* WLH 4 Nov 98 */
      if (b1 && uctx) {
         deallocate( uctx, dtx->VWindTable[ws][time].verts, b1 );
      }
      b2 = dtx->VWindTable[ws][time].numboxverts * 3 * sizeof(float);
/* WLH 4 Nov 98
      if (b2) {
*/
      /* WLH 4 Nov 98 */
      if (b2 && uctx) {
         deallocate( uctx, dtx->VWindTable[ws][time].boxverts, b2 );
      }
      dtx->VWindTable[ws][time].valid = 0;
      return b1 + b2;
   }
   else {
      return 0;
   }
}



int free_hstream( Display_Context dtx, int time, int ws )
{
   Context uctx;
   
   uctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->HStreamTable[ws][time].valid) {
      int b1, b2;
/* MJK 10.14.98       
      dtx->DisplayHStream[ws] = 0;
*/
      b1 = dtx->HStreamTable[ws][time].nlines * sizeof(int_2) * 3;
/* WLH 4 Nov 98
      if (b1) {
*/
      /* WLH 4 Nov 98 */
      if (b1 && uctx) {
         deallocate( uctx, dtx->HStreamTable[ws][time].verts, b1 );
      }
      b2 = dtx->HStreamTable[ws][time].numboxverts * 3 * sizeof(float);
/* WLH 4 Nov 98
      if (b2) {
*/
      /* WLH 4 Nov 98 */
      if (b2 && uctx) {
         deallocate( uctx, dtx->HStreamTable[ws][time].boxverts, b2 );
      }
      dtx->HStreamTable[ws][time].valid = 0;
      return b1 + b2;
   }
   else {
      return 0;
   }
}



int free_vstream( Display_Context dtx, int time, int ws )
{

   Context uctx;

   uctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->VStreamTable[ws][time].valid) {
      int b1, b2;
/* MJK 10.14.98       
      dtx->DisplayVStream[ws] = 0;
*/
      b1 = dtx->VStreamTable[ws][time].nlines * sizeof(int_2) * 3;
/* WLH 4 Nov 98
      if (b1) {
*/
      /* WLH 4 Nov 98 */
      if (b1 && uctx) {
         deallocate( uctx, dtx->VStreamTable[ws][time].verts, b1 );
      }
      b2 = dtx->VStreamTable[ws][time].numboxverts * 3 * sizeof(float);
/* WLH 4 Nov 98
      if (b2) {
*/
      /* WLH 4 Nov 98 */
      if (b2 && uctx) {
         deallocate( uctx, dtx->VStreamTable[ws][time].boxverts, b2 );
      }
      dtx->VStreamTable[ws][time].valid = 0;
      return b1 + b2;
   }
   else {
      return 0;
   }
}



void free_all_graphics( Context ctx )
{
   int set, var, time, ws;

   for (var=0;var<ctx->NumVars;var++) {
	  free_param_graphics( ctx, var );
   }
	if(ctx->dpy_ctx){
	  for (time=0;time< ctx->dpy_ctx->NumTimes; time++) {
		 for (ws=0;ws<VIS5D_WIND_SLICES;ws++) {
         if (ctx->dpy_ctx->Uvarowner[ws] == ctx->context_index){
			  free_hwind( ctx->dpy_ctx, time, ws );
			  free_vwind( ctx->dpy_ctx, time, ws );
			  free_hstream( ctx->dpy_ctx, time, ws );
			  free_vstream( ctx->dpy_ctx, time, ws );
         }
		 }
	  }
	  if (ctx->context_index == ctx->dpy_ctx->TrajUowner){
		 for (set = 0; set < VIS5D_TRAJ_SETS; set++){
         ctx->dpy_ctx->DisplayTraj[set] = 0;
         vis5d_delete_traj_set(ctx->dpy_ctx->dpy_context_index, set);
		 }
	  }
	}
}

void turn_off_and_free_var_graphics( Context ctx, int var)
{
   int set, i, time;
   free_param_graphics( ctx, var );

   for (i=0;i<VIS5D_WIND_SLICES;i++) {
      if ((ctx->context_index == ctx->dpy_ctx->Uvarowner[i]) &&
          (var == ctx->dpy_ctx->Uvar[i] ||
           var == ctx->dpy_ctx->Vvar[i] ||
           var == ctx->dpy_ctx->Wvar[i])){
         for (time = 0; time < ctx->dpy_ctx->NumTimes; time++){
            free_hwind( ctx->dpy_ctx, time, i);
            free_vwind( ctx->dpy_ctx, time, i);
            free_hstream( ctx->dpy_ctx, time, i);
            free_vstream( ctx->dpy_ctx, time, i);
         }
      }
   }
   if ((ctx->context_index == ctx->dpy_ctx->TrajUowner) &&
          (var == ctx->dpy_ctx->TrajU ||
           var == ctx->dpy_ctx->TrajV ||
           var == ctx->dpy_ctx->TrajW) ){
      for (set = 0; set < VIS5D_TRAJ_SETS; set++){
         vis5d_delete_traj_set(ctx->dpy_ctx->dpy_context_index, set);
      }
   }
}



/* BUG FIX MJK 8.10.98 */
/* Added this function so when ever dtx map and vert parameters */
/* are changed, graphics will but turned off and deallocated */
void turn_off_and_free_all_display_graphics( Display_Context dtx )
{
   int yo, var, time, set, ws;
   Context ctx;
   
   /* param graphics */
   for (yo = 0; yo < dtx->numofctxs; yo++){
      ctx = dtx->ctxpointerarray[yo];
      for (var=0;var<ctx->NumVars;var++) {
         free_param_graphics( ctx, var );
         vis5d_enable_graphics(ctx->context_index, VIS5D_ISOSURF, var, VIS5D_OFF);
         vis5d_enable_graphics(ctx->context_index, VIS5D_HSLICE, var, VIS5D_OFF);
         vis5d_enable_graphics(ctx->context_index, VIS5D_VSLICE, var, VIS5D_OFF);
         vis5d_enable_graphics(ctx->context_index, VIS5D_CHSLICE, var, VIS5D_OFF);
         vis5d_enable_graphics(ctx->context_index, VIS5D_CVSLICE, var, VIS5D_OFF);
         vis5d_enable_graphics(ctx->context_index, VIS5D_VOLUME, var, VIS5D_OFF);
      }
   }

   /* traj sets */
   for (set = 0; set < VIS5D_TRAJ_SETS; set++){
      vis5d_delete_traj_set(dtx->dpy_context_index, set);
      dtx->DisplayTraj[set] = 0; 
   }
  
   /* U,V,W graphics */
   for (ws=0;ws<VIS5D_WIND_SLICES;ws++) {
      for (time=0;time<dtx->NumTimes;time++) {
         free_hwind( dtx, time, ws );
         free_vwind( dtx, time, ws );
         free_hstream( dtx, time, ws );
         free_vstream( dtx, time, ws );
      }
      dtx->DisplayHWind[ws] = 0;
      dtx->DisplayVWind[ws] = 0;
      dtx->DisplayHStream[ws] = 0;
      dtx->DisplayVStream[ws] = 0;
   }
}
         

static int AccessTime = 1;   /* access time pseudo-clock */



/*** recent ***********************************************************
   Inform the memory manager of the most recently used graphic.
   Input:  ig - the graphic type: ISOSURF, HSLICE, VWIND, TRAJ, etc.
           ip - which parameter, wind slice, traj group, etc.
**********************************************************************/
void recent( Context ctx, int ig, int ip )
{
   switch (ig) {
      case ISOSURF:
         ctx->RecentIsosurf[ip] = AccessTime;
         break;
      case HSLICE:
         ctx->RecentHSlice[ip] = AccessTime;
         break;
      case VSLICE:
         ctx->RecentVSlice[ip] = AccessTime;
         break;
      case CHSLICE:
         ctx->RecentCHSlice[ip] = AccessTime;
         break;
      case CVSLICE:
         ctx->RecentCVSlice[ip] = AccessTime;
         break;
      case HWIND:
         ctx->dpy_ctx->RecentHWind[ip] = AccessTime;
         break;
      case VWIND:
         ctx->dpy_ctx->RecentVWind[ip] = AccessTime;
         break;
      case HSTREAM:
         ctx->dpy_ctx->RecentHStream[ip] = AccessTime;
         break;
      case VSTREAM:
         ctx->dpy_ctx->RecentVStream[ip] = AccessTime;
         break;
      case TRAJ:
         ctx->dpy_ctx->RecentTraj[ip] = AccessTime;
         break;
      default:
         printf("error in recent( %d, %d )\n", ig, ip );
   }
   AccessTime++;
}




/* because (val % limit) doesn't work for negatives */
static int adjust( int val, int limit )
{
  while (val < 0) val += limit;
  while (val >= limit) val -= limit;
  return val;
}




/*
 * Free all graphics for a particular time step.
 * Return:  number of bytes freed or 0 if nothing found to deallocate.
 *
 * NOTE: time is in ctx time
 */
static int free_time( Context ctx, int time )
{

   int var, ws, bytes;
   int ctime, t, vtime;
   Display_Context dtx;

   dtx = ctx->dpy_ctx;
   bytes = 0;
   LOCK_ON( GfxLock );
  
   for (ws=0;ws<VIS5D_WIND_SLICES;ws++) {
      if (dtx->Uvarowner[ws] == ctx->context_index){
         for(t=0; t < dtx->NumTimes; t++){
            ctime = dtx->TimeStep[t].ownerstimestep[return_ctx_index_pos(dtx,
                                                 ctx->context_index)];
            if (ctime==time){
               bytes += free_hwind( dtx, t, ws);
               bytes += free_vwind( dtx, t, ws);
               bytes += free_hstream( dtx, t, ws);
               bytes += free_vstream( dtx, t, ws);
            }
         }
      }
   }

   vtime = time; /* WLH 26 Jan 2000 */

   for (var=0;var<ctx->NumVars;var++) {
      /* isosurfaces */
      bytes += free_isosurface( ctx, vtime, var );
 
      /* horizontal contour slices */
      bytes += free_hslice( ctx, vtime, var );
 
      /* vertical contour slices */
      bytes += free_vslice( ctx, vtime, var );

      /* horizontal colored slices */
      bytes += free_chslice( ctx, vtime, var );
  
      /* vertical colored slices */
      bytes += free_cvslice( ctx, vtime, var );
   }
   LOCK_OFF( GfxLock );
   return bytes;
}


int i_deallocate_lru( Irregular_Context itx )
{
   /* this has not been implemented yet, since so far there is */
   /* only one graphic displayable for an irregular data context */
   return 0;
}






   
/*** deallocate_lru ***************************************************
   Find and deallocate the least-recently-used graphic.
   Return:  number of bytes freed or 0 if nothing found to deallocate.
**********************************************************************/
int deallocate_lru( Context ctx )
{
   int var, time, t, oldtime, oldig, oldvar, bytes;
   static int first_time=1;
   Display_Context dtx;

   dtx = ctx->dpy_ctx;

   if (first_time) {
      printf("WARNING:  Memory is full, graphics may be discarded\n");
      first_time = 0;
   }

   oldtime = AccessTime;

  if (ctx->VeryLarge) {
    int tp, tm;

    tm = adjust(ctx->CurTime + ctx->NumTimes / 2, ctx->NumTimes);
    tp = adjust(tm + 1, ctx->NumTimes);
    bytes = 0;
    while ((tp != ctx->CurTime || tm != ctx->CurTime) && bytes == 0) {
      if (tp != ctx->CurTime) {
        bytes += free_time( ctx, tp);
        tp = adjust(tp + 1, ctx->NumTimes);
      }
      if (tm != ctx->CurTime) {
        bytes += free_time( ctx,  tm);
        tm = adjust(tm - 1, ctx->NumTimes);
      }
    }
    if (bytes == 0) {
      /*printf("free_lru() failed\n");*/
    }
    return bytes;
  }
  else {
     /* find oldest isosurface */
     for (var=0;var<ctx->NumVars;var++) {
        t = ctx->RecentIsosurf[var];
        if (t>0 && t<oldtime) {
           oldtime = t;
           oldig = ISOSURF;
           oldvar = var;
        }
     }

     /* find oldest horizontal contour slice */
     for (var=0;var<ctx->NumVars;var++) {
        t = ctx->RecentHSlice[var];
        if (t>0 && t<oldtime) {
           oldtime = t;
           oldig = HSLICE;
           oldvar = var;
        }
     }

     /* find oldest vertical contour slice */
     for (var=0;var<ctx->NumVars;var++) {
        t = ctx->RecentVSlice[var];
        if (t>0 && t<oldtime) {
           oldtime = t;
           oldig = VSLICE;
           oldvar = var;
        }
     }

     /* find oldest horizontal colored slice */
     for (var=0;var<ctx->NumVars;var++) {
        t = ctx->RecentCHSlice[var];
        if (t>0 && t<oldtime) {
           oldtime = t;
           oldig = CHSLICE;
           oldvar = var;
        }
     }

     /* find oldest vertical colored slice */
     for (var=0;var<ctx->NumVars;var++) {
        t = ctx->RecentCVSlice[var];
        if (t>0 && t<oldtime) {
           oldtime = t;
           oldig = CVSLICE;
           oldvar = var;
        }
     }

     /* find oldest horizontal wind slice */
     for (var=0;var<VIS5D_WIND_SLICES;var++) {
        if (dtx->Uvarowner[var] == ctx->context_index){
           t = dtx->RecentHWind[var];
           if (t>0 && t<oldtime) {
              oldtime = t;
              oldig = HWIND;
              oldvar = var;
           }
        }
     }

     /* find oldest vertical wind slice */
     for (var=0;var<VIS5D_WIND_SLICES;var++) {
        if (dtx->Uvarowner[var] == ctx->context_index){
           t = dtx->RecentVWind[var];
           if (t>0 && t<oldtime) {
              oldtime = t;
              oldig = VWIND;
              oldvar = var;
           }
        }
     }
     /* find oldest horizontal stream slice */
     for (var=0;var<VIS5D_WIND_SLICES;var++) {
        if (dtx->Uvarowner[var] == ctx->context_index){
           t = dtx->RecentHStream[var];
           if (t>0 && t<oldtime) {
              oldtime = t;
              oldig = HSTREAM;
              oldvar = var;
           }
        }
     }

     /* find oldest vertical stream slice */
     for (var=0;var<VIS5D_WIND_SLICES;var++) {
        if (dtx->Uvarowner[var] == ctx->context_index){
           t = dtx->RecentVStream[var];
           if (t>0 && t<oldtime) {
              oldtime = t;
              oldig = VSTREAM;
              oldvar = var;
           }
        }
     }

     /* find oldest trajectory group */
     for (var=0;var<VIS5D_TRAJ_SETS;var++) {
        if (dtx->TrajUowner == ctx->context_index){
           t = dtx->RecentTraj[var];
           if (t>0 && t<oldtime) {
              oldtime = t;
              oldig = TRAJ;
              oldvar = var;
           }
        }
     }


     if (oldtime<AccessTime) {
        /* found something to deallocate */
        bytes = 0;

        if (oldig==ISOSURF) {
           /* deallocate all 'oldvar' isosurfaces */
           for (time=0;time<ctx->NumTimes;time++) {
              bytes += free_isosurface( ctx, time, oldvar );
              /*printf("[reclaimed %d] isosurf %d %d\n", bytes,oldvar,time);*/
           }
           ctx->RecentIsosurf[oldvar] = 0;
        }
        else if (oldig==HSLICE) {
           /* dealloc all 'oldvar' horizontal contour slices */
           for (time=0;time<ctx->NumTimes;time++) {
              bytes += free_hslice( ctx, time, oldvar );
              /*printf("[reclaimed %d] hslice %d %d\n", bytes,oldvar,time);*/
           }
           ctx->RecentHSlice[oldvar] = 0;
        }
        else if (oldig==VSLICE) {
           /* dealloc all 'oldvar' horizontal contour slices */
           for (time=0;time<ctx->NumTimes;time++) {
              bytes += free_vslice( ctx, time, oldvar );
              /*printf("[reclaimed %d] vslice %d %d\n", bytes,oldvar,time);*/
           }
           ctx->RecentVSlice[oldvar] = 0;
        }
        else if (oldig==CHSLICE) {
           for (time=0;time<ctx->NumTimes;time++) {
              bytes += free_chslice( ctx, time, oldvar );
              /*printf("[reclaimed %d] chslice %d %d\n", bytes,oldvar,time);*/
           }
           ctx->RecentCHSlice[oldvar] = 0;
        }
        else if (oldig==CVSLICE) {
           for (time=0;time<ctx->NumTimes;time++) {
              bytes += free_cvslice( ctx, time, oldvar );
              /*printf("[reclaimed %d] cvslice %d %d\n", bytes,oldvar,time);*/
           }
           ctx->RecentCVSlice[oldvar] = 0;
        }
        else if (oldig==HWIND) {
           for (time=0;time<dtx->NumTimes;time++) {
              bytes += free_hwind( dtx, time, oldvar );
              /*printf("[reclaimed %d] hwind %d\n", bytes*3,time);*/
           }
           dtx->RecentHWind[oldvar] = 0;
        }
        else if (oldig==VWIND) {
           for (time=0;time<dtx->NumTimes;time++) {
              bytes += free_vwind( dtx, time, oldvar );
              /*printf("[reclaimed %d] vwind %d\n", bytes*3,time );*/
           }
           dtx->RecentVWind[oldvar] = 0;
        }
        else if (oldig==HSTREAM) {
           for (time=0;time<dtx->NumTimes;time++) {
              bytes += free_hstream( dtx, time, oldvar );
              /*printf("[reclaimed %d] hstream %d\n", bytes*3,time);*/
           }
           dtx->RecentHStream[oldvar] = 0;
        }
        else if (oldig==VSTREAM) {
           for (time=0;time<dtx->NumTimes;time++) {
              bytes += free_vstream( dtx, time, oldvar );
              /*printf("[reclaimed %d] vstream %d\n", bytes*3,time);*/
           }
           dtx->RecentVStream[oldvar] = 0;
        }
        else if (oldig==TRAJ) {
           del_traj_group( dtx, oldvar );
           bytes = 1;
           dtx->RecentTraj[oldvar] = 0;
        }
        else {
           printf("oldig=%d\n", oldig );
           die("error in deallocate_lru");
        }

        if (bytes==0)
           bytes = 1;
        return bytes;
     }
     else {
        /*printf("free_lru() failed\n");*/
        return 0;
     }
  }
}




syntax highlighted by Code2HTML, v. 0.9.1