#include "copyright.h"
/****************************************************************** 
 *
 * indexclass.c --
 *
 *     Implementation of index-file that goes with a log file.
 *
 * $Author: mstoufer $
 * $Source: /home/g2/src/projects/NetLogger/src/nlv/indexclass.c,v $
 * $RCSfile: indexclass.c,v $
 * $Revision: 1.1.1.1 $
 * $Locker:  $
 * $Date: 2000/12/05 19:52:32 $
 *
 ****************************************************************** 
 */
static char rcsid[] = "$Id: indexclass.c,v 1.1.1.1 2000/12/05 19:52:32 mstoufer Exp $";

#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "indexclass.h"
#include "syswrappers.h"

#ifdef IRIX_SUCKS
#undef _POSIX_THREAD_PRIORITY_SCHEDULING
#endif

#ifndef _POSIX_THREAD_PRIORITY_SCHEDULING
#include <sys/types.h>
#include <sys/time.h>
#endif

/*========= MACROS ===================================================*/

#define LOCK_MUTEX(m) if ( 0 != pthread_mutex_lock( (m) ) ) { \
                        Nlv_EInfo einfo; \
			NLV_SET_EINFO(einfo,0,0); \
			Nlv_errReport( eobj2, &einfo, "Locking mutex failed" ); \
                      } 
#define UNLOCK_MUTEX(m) if ( 0 != pthread_mutex_unlock( (m) ) ) { \
                        Nlv_EInfo einfo; \
			NLV_SET_EINFO(einfo,0,0); \
			Nlv_errReport( eobj2, &einfo, "Unlocking mutex failed" ); \
                      } 


/*========= DATA =====================================================*/

typedef struct densityData {
  long count;  /* # of data points    */
  double time; /* beg. of time window */
} densityData;

#define T Nlv_ix
struct Nlv_ix {
  FILE *fptr;
  int   logfd;
  char *name;
  long pos;
  double timePos;
  double start;
  double showPos;
  double jumpTime;
  int    wasStale;
  long increment;
  struct curdensity {  /* Density 'bar' information: */
    densityData *arr;  /* array of bars */
    int          sz;   /* capacity of array */
    int          num;  /* number of bars in array */
    double       ival; /* time interval spanned by each bar (s) */
    long         cnt;  /* number of data points in current bar */
    double       tlast;/* last time a bar was added */
    long         hcnt; /* highest count so far */
    long         hwcnt;/* highest count for window */
  } d;
  pthread_mutex_t mutex;
  pthread_mutex_t posMutex;
  pthread_mutex_t cntMutex;
  pthread_attr_t  tattr;
  int             usecDelay;
};

extern Nlv_err eobj2;

#define FILE_ERR_CODE 1000

/*========= FUNCTIONS ================================================*/

long   Nlv_ixGetIndexPosition( T, double t );

void   Nlv_ixSetShowPos( T , double newval );

void   Nlv_ixInitDensity( T, double ival, double start_time );
void   Nlv_ixUpdateDensity( T );
int    Nlv_ixAddToDensity( T, double at_time_t );
void   Nlv_ixResetDensity( T, double new_last_time );
void   Nlv_ixEnterDensity( T );

/*
 *----------------------------------------------------------------------
 *
 * New -- Allocate new object. Associate with log file.
 *
 * Calls: malloc
 *
 * Algorithm: 0
 *
 *---------------------------------------------------------------------- */

T Nlv_ixNew( )
{
  char *func = "Nlv_ixNew";
  T this;
  int status;

  this = (T)Nlv_Malloc2( sizeof( struct Nlv_ix ) );
  this->fptr      = NULL;
  this->name      = NULL;
  this->pos       = 0L;
  this->timePos   = 0.;
  this->jumpTime  = 0.;
  this->increment = 1000;
  this->wasStale  = 0;
  this->start     = -1;
  this->showPos   = 0;
  this->d.num     = 0;
  if ( pthread_mutex_init( &this->mutex, NULL ) != 0    ||
       pthread_mutex_init( &this->posMutex, NULL ) != 0 ||
       pthread_mutex_init( &this->cntMutex, NULL ) != 0 ) {
    Nlv_errPrintString( eobj2, func, "Init mutex failed.");
    exit( 1 );
  }

  /* #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
   * Init the attribute object for indexer's thread 
   * pthread_attr_init(&(this->tattr));
   * pthread_attr_setinheritsched( &(this->tattr), PTHREAD_EXPLICIT_SCHED );
   * pthread_attr_setschedpolicy( &(this->tattr), SCHED_RR );
   *
   *
   * #endif
   */

  /* Nlv_ixNormalPriority( this ); */
  Nlv_ixIncreasePriority( this ); /* start with indexer at full blast */

  return this;
}

/*
 *----------------------------------------------------------------------
 *
 * InitDensity -- Initialize density array and counters
 *
 * Calls:
 *
 * Algorithm: Must be called after start time in file is known.
 *
 *----------------------------------------------------------------------
 */
void Nlv_ixInitDensity( T this,
			double ival,  /* interval (s) */
			double stime )/* start time in file */
{
  this->d.ival  = ival;
  this->d.tlast = 0.;
  this->d.sz    = 128;
  this->d.arr   = (densityData *) malloc( this->d.sz * sizeof(densityData) );
  this->d.num   = 0;
  this->d.cnt   = 0;
  this->d.hcnt  = 0;
  this->d.hwcnt = 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Init -- Initialize indexer state.
 *
 * Calls    :
 *
 * Algorithm: - open log file
 *            - initialize density-bars
 *            - open indexer file
 *            - get last time in indexer file
 *
 *----------------------------------------------------------------------
 */
int Nlv_ixInit( T      this,
		double ival,   /* indexing interval */
		double d_ival, /* summary-line interval */
		char  *fname)/* filename of log file */
{
  double d_ival_seconds = d_ival/1000.0;
  Nlv_EInfo dbginfo,  /* debug, error reporting objs */ 
            einfo;
  
  NLV_SET_EINFO( dbginfo, NLV_DEBUG_LVL, 0 );
  
  /* open log file */
  this->logfd = open( fname, O_RDONLY );
  
  /* abort on open error */
  if ( this->logfd < 0 ) {
    NLV_SET_EINFO( einfo, FILE_ERR_CODE+1, 0 );
    Nlv_errReport( eobj2, &einfo, "Unable to open log file from indexer" );
    return -1;
  }
  
  Nlv_ixInitDensity( this, d_ival_seconds, this->start );
  Nlv_ixSetIncrement( this, ival );
  /* open indexer file */
  if ( -1 == Nlv_ixOpen( this, fname ) )
    return -1;
  /* get last time in indexer file, so we append at the right place */
  this->timePos = Nlv_ixGetLastTime( this );
  
  if ( !this->wasStale ) {
    printf("Using previously created index file..\n" );
    this->timePos = Nlv_ixGetLastTime( this );
  }
  else
    printf("Creating new index file..\n");
  
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Run -- Run the indexer as a thread.
 *
 * Calls    :
 *
 * Algorithm: Wait for main program to set the starting time.
 *            Index when needed.
 *            Every XX milliseconds, add an entry to a list, so the
 *               GUI can display "density" info.
 *            Take a little nap when EOF is reached.
 *            Stop when main program sets start to
 *              -1 again.
 *
 *----------------------------------------------------------------------
 */
void * Nlv_ixRun ( void * args )
{
  char         ln[ 1024 ];      /* line of data */
  int          length;          /* length of line returned */
  char        *p;               /* position of start of log entry */
  double       tm;              /* time of line */
  double       reltm;           /* time - firsttime */
  long         rpos;            /* beginning of line */
  long         cnt;             /* # of lines read */
  int          sleepy;          /* went to sleep on last cycle */
  int          ret;             /* return value */
  long         count;           /* # times thu loop */
  Nlv_EInfo    dbginfo;         /* debugging msg level/code */ 
  Nlv_EInfo    einfo;           /* error msg level/code */
  Nlv_ixArgs * a;               /* alias for input args */
  Nlv_ix       this;            /* I, Claudius */
  
  /* set some initial var values */
  a               = (Nlv_ixArgs *)args;
  this            = a->obj;
  sleepy          = 0;
  cnt             = 0;
  tm              = 0.;
  p               = &ln[0];
  length          = 0;
  reltm           = 0;
  count           = 0;
  memset( ln, 0, sizeof(ln) );

  /* special code -1 clears the function's state */
  fdread1line( -1, &ln[0], &p, &length, sizeof(ln) );
  /* Start at beginning of file! */
  rpos = lseek( this->logfd, 0, SEEK_SET );
  if ( rpos != 0 ) {
    NLV_FATAL_S2( "During lseek in log file", strerror(errno) );
    exit(1);
  }

  /* Cleanup routine */
  pthread_cleanup_push( (void*)Nlv_ixCleanup, (void*)this );

  /* ----- begin main loop ----- */
  while( 1 ) 
  {
      /* ensure that thread may be canceled at least once during loop */
      pthread_testcancel();

    /* read data */
    if ( !fdread1line( this->logfd, &ln[0], &p, &length, sizeof(ln)) ) {
      if ( !sleepy ) {
	Nlv_ixSetShowPos( this, reltm);/* update position on line */
	sleepy = 1;                    /* next time we will skip this */
      }
      select_sleep( 0, 1000 );    /* sleep for a little while */
    }
    else {
      Nlv_paGetLineTime( p, &tm );
      if ( tm >= 0 ) {
	if ( sleepy ) {
	  this->jumpTime = tm;
	  sleepy = 0;
	}
	reltm  = tm - this->start;
	if ( reltm < NLV_MAX_BACKWARDS ) {
	  NLV_SET_EINFO( einfo, FILE_ERR_CODE+5, 0 );
	  Nlv_errReport( eobj2, &einfo,"Time in log file is too far out of "
			 "order.\nTime=%f , Start Time=%f. Difference=%f\n",
			 tm, this->start, reltm );
          select_sleep( 0, this->usecDelay );
          continue;
	}
	while ( Nlv_ixNeedsAppend( this, tm ) ) {
	  /* why +1?? Nlv_ixAppend( this, rpos+1 ); */
	  Nlv_ixAppend( this, rpos );
	  if (cnt++ % 100 == 0 )
	    Nlv_ixSetShowPos( this, reltm);
        }
      }
      /* first, add "bars" if we are beyond right edge of window */
      while ( Nlv_ixAddToDensity( this, reltm ) ) {
	Nlv_ixEnterDensity( this );
	Nlv_ixResetDensity( this, reltm );
      }
      /* then add to count for this window */
      Nlv_ixUpdateDensity( this );
      rpos += length;
      if ( ! (++count % 100 ) )
      Nlv_ixSetShowPos( this, reltm); /* update position on line */
      /* then take a little nap, grandpa */
      select_sleep( 0, this->usecDelay );
    } 
  }
  /* ----- end main loop ----- */

  /* we should never get here! */
  assert(0);
  /* Need to match cleanup push macro */
  pthread_cleanup_pop( 0 );
  return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Open -- Open index file for read/write
 *
 * Calls: fopen
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

int  Nlv_ixOpen( T this, char *logFile )
{
  struct stat ixTime, logTime;
  char *dir, *last_slash;
  char try_name[ 1024 ];
  FILE *try_fptr;
  Nlv_EInfo einfo;

  /*
   * Try to find an index filename that is
   * readable/writeable. Try in this order:
   *   - same directory as log file
   *   - current directory
   *   - /tmp
   *   - ABORT
   */

  if ( logFile[strlen(logFile)-1] == '/' ) {
    NLV_FATAL_S( "Unable to open index file: logfile is a directory!\n");
    return -1;
  }
  if ( (last_slash = strrchr( logFile, '/' )) == NULL )
    dir = strdup(".");
  else {
    dir         = logFile;
    *last_slash = '\0';
    logFile     = last_slash+1;
  }
  strcpy( try_name, dir );
  strcat( try_name, "/" );
  strcat( try_name, logFile );
  strcat( try_name, ".idx" );
  fprintf(stderr,"Trying to open index file in same dir as log file\n");
  this->wasStale = 0;
  if ( NULL == (try_fptr = fopen( try_name, "r+" )) )
    if ( NULL != (try_fptr = fopen( try_name, "w+" )) ) 
       this->wasStale = 1;
    else if ( strstr(dir,"./") != dir ) {
        strcpy( try_name, "./" );
        strcat( try_name, logFile );
        strcat( try_name, ".idx" );
        fprintf(stderr,"Trying to open index file in current directory\n");
        if ( NULL == (try_fptr = fopen( try_name, "r+" )) )
          if ( NULL != (try_fptr = fopen( try_name, "w+" )) )
            this->wasStale = 1;
    }
    if ( !try_fptr ) {
     strcpy( try_name, "/tmp/");
     strcat( try_name, logFile );
     strcat( try_name, ".idx" );
     fprintf(stderr,"Trying to open index file in /tmp\n");
     if ( NULL == (try_fptr = fopen( try_name, "r+" )) )
       if ( NULL != (try_fptr = fopen( try_name, "w+" )) )
         this->wasStale = 1;
       else { 
         NLV_FATAL_S( "Unable to open index file in logfile directory,\n"
                    "current directory, or /tmp");
         return -1;
       }
    }
  

  this->name = strdup( try_name );
  this->fptr = try_fptr;
  fprintf(stderr,"Success! Using index file '%s'\n", this->name );

  /* 
   * If index file existed previously, only overwrite if stale.
   */

   if ( 0 == this->wasStale ) {
    if ( 0 != stat( this->name,&ixTime ) )
      return -1;
    stat( logFile, &logTime );
    if ( ixTime.st_mtime < logTime.st_mtime ) {
      fprintf(stderr,"Index file '%s' is stale. Overwriting..\n", this->name ); 
      this->fptr = fopen( this->name, "w+" );
      this->wasStale = 1;
    }
    else {
      this->fptr = freopen( this->name, "a+", this->fptr );
      this->wasStale = 0;
    }
  }

  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Cleanup -- Clean up before exiting thread
 *
 * Calls: fclose
 *
 * Algorithm: 0
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixCleanup( void *thisvoid )
{
  T this = (T)thisvoid;

  printf("Cleaning up index thread...\n");
  assert( this->fptr);
  fclose( this->fptr );
}

/*
 *----------------------------------------------------------------------
 *
 * SetStart -- First time in log file.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixSetStart( T this, double time )
{
  this->start   = time;
  this->timePos = time;
}


/*
 *----------------------------------------------------------------------
 *
 * GetLastTime -- Return the time in the log file which
 *                is at the last indexed position in the index file.
 *
 * Calls    :
 *
 * Algorithm: start should be already set.
 *            Then, the last time = startpos + #bytes/16 * increment
 *
 *----------------------------------------------------------------------
 */
double Nlv_ixGetLastTime( T this ) 
{
  Nlv_EInfo einfo;
  long p, save;
  double retval = 0.;

  LOCK_MUTEX( &(this->mutex) );

  save = ftell( this->fptr );
  if ( (p = (long)fseek(this->fptr, 0, SEEK_END)) < 0 ||
       (p = ftell(this->fptr)) < 0 ) {
    NLV_SET_EINFO( einfo, FILE_ERR_CODE+3,0 );
    Nlv_errReport( eobj2, &einfo, "Cannot read last position in index file" );
  }
  else
    retval = this->start + (p / 16) * this->increment;
  fseek( this->fptr, save, SEEK_SET );

  UNLOCK_MUTEX( &(this->mutex) );

  return retval;
}

/*
 *----------------------------------------------------------------------
 *
 * SetIncrement -- Indexing increment in seconds.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixSetIncrement( T this, double s )
{
  this->increment = s;
}


/*
 *----------------------------------------------------------------------
 *
 * GetPosition -- Position in log file for time ( time is in ms ).
 *
 * Calls: fseek, fscanf, ftell
 *
 * Algorithm: Go there and retrieve the spot in the log file.
 *
 *----------------------------------------------------------------------
 */

long Nlv_ixGetPosition( T this, double time )
{
  long oldp;
  long newp;
  long logp = 0L;
  int x;
  Nlv_EInfo einfo;

  LOCK_MUTEX( &(this->mutex) );

  oldp = ftell( this->fptr );
  newp = Nlv_ixGetIndexPosition(this,time) * 16L;

  /* 
   * Index file entry #0 ==> increment #1
   *       anything < 0 ==> 0 
   */

  if ( newp < 0 ) {
    UNLOCK_MUTEX( &(this->mutex) );
    return 0L;
  }
  if ( 0 != fseek( this->fptr, newp+1, SEEK_SET ) ) {
    NLV_SET_EINFO( einfo, FILE_ERR_CODE+4, 0 );
    Nlv_errReport( eobj2, &einfo, "Cannot seek to %d in log file",
		   (int)(newp+1) );
    logp = -1;
  }
  else {
    if ( fscanf( this->fptr, "%ld", &logp) != 1 ) {
      logp = -1;
    }
  }
  fseek( this->fptr, oldp, SEEK_SET );

  UNLOCK_MUTEX( &(this->mutex) );

  return logp;
}

long   Nlv_ixGetIncrement( T this ) 
{
  return this->increment;
}

/*
 *----------------------------------------------------------------------
 *
 * GetIndexPosition -- Get # of index entry.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

long Nlv_ixGetIndexPosition( T this, double t ) 
{
  return (double)(t - this->start) / (double)this->increment - 1;
}

/*
 *----------------------------------------------------------------------
 *
 * GetLastPosition -- Get last indexed position
 *
 * Calls: fseek, fscanf, ftell
 *
 * Algorithm: The last position is just one entry-width back from
 *            end.
 *
 *----------------------------------------------------------------------
 */

long Nlv_ixGetLastPosition( T this )
{
  long oldp, logp;
  Nlv_EInfo dbginfo;
  
  NLV_SET_EINFO( dbginfo, NLV_DEBUG_LVL, 0 );

  LOCK_MUTEX( &(this->mutex) );

  oldp = ftell( this->fptr );
  logp = 0L;
  if ( fseek( this->fptr, -16, SEEK_END ) != 0 )
    logp = 0L;
  else
    fscanf( this->fptr, "%16ld", &logp );
  fseek( this->fptr, oldp, SEEK_SET );

  UNLOCK_MUTEX( &(this->mutex) );

  Nlv_errReport( eobj2, &dbginfo, "Last position = %l", logp );
  return logp;
}


/*
 *----------------------------------------------------------------------
 *
 * Append -- Append to index file.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

int  Nlv_ixAppend( T this, long pos )
{
  LOCK_MUTEX( &(this->mutex) );

  fprintf(this->fptr, "%16ld", pos );
  fflush( this->fptr );

  UNLOCK_MUTEX( &(this->mutex) );

  this->timePos += (double)this->increment;
  return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * NeedsAppend -- Does the given time exceed the last index?
 *
 * Calls    :
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

int Nlv_ixNeedsAppend( T this, double t )
{
  Nlv_EInfo einfo;

  NLV_SET_EINFO( einfo, NLV_DEBUG_LVL, 0 );
  if ( t > this->timePos ) {
      Nlv_errReport(eobj2, &einfo,
		  "Needs index append because %f > %f", t, this->timePos );
    return 1;
  }
  else { 
    /*        Nlv_errReport( eobj2, &einfo,
	      "Doesn't need index append because !(%f > %f)", t, this->timePos );
    */
    return 0;
  }
}

/*
 *----------------------------------------------------------------------
 *
 * WasStale -- Was the index file stale on open (thus need re-
 *             doing? ).
 *
 * Calls    :
 *
 * Algorithm: Accessor to variable of same name.
 *
 *----------------------------------------------------------------------
 */

int Nlv_ixWasStale( T this )
{
  return this->wasStale;
}


/*
 *----------------------------------------------------------------------
 *
 * SetShowPos -- Set the show-pos variable
 *
 * Calls    :
 *
 * Algorithm: Use posMutex to guarantee thread-safety
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixSetShowPos( T this, double newval )
{
  LOCK_MUTEX( &(this->posMutex) );
  if ( newval >= 0 )
    this->showPos = newval;
  UNLOCK_MUTEX( &(this->posMutex) );
}


/*
 *----------------------------------------------------------------------
 *
 * GetShowPos -- Get the show-pos variable
 *
 * Calls    :
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

double Nlv_ixGetShowPos( T this )
{
  return this->showPos;
}

/*
 *----------------------------------------------------------------------
 *
 * GetTimePos -- Get the position of the indexer in the file (which is
 *               the last time in the file rounded down to the indexing
 *               increment, if all is going well )
 *
 * Calls    :
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
double Nlv_ixGetTimePos( T this )
{
  return this->timePos;
}


/*
 *----------------------------------------------------------------------
 *
 * GetTattr -- Get thread attributes for indexer.
 *
 * Algorithm: Accessor
 *
 *----------------------------------------------------------------------
 */

pthread_attr_t * Nlv_ixGetTattr( T this )
{
  /*  #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
   * return &(this->tattr);
   * #else
   */
  return NULL;
  /* #endif */
}


/*
 *----------------------------------------------------------------------
 *
 * IncreasePriority -- Increase the indexing thread's priority. This
 *                     method is called by the main thread when it
 *                     needs the indexer to "catch up" to the current
 *                     position.
 *
 * Algorithm: If (POSIX) thread scheduling is present, increase the thread
 *            priority to the maximum. Otherwise, reduce the sleep
 *            which is used to simulate thread priority to 0.
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixIncreasePriority( T this ) 
{ 
  /*  #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
   *
   *  struct sched_param rr_param;
   *  int rr_max_prio = sched_get_priority_max( SCHED_RR );
   *  rr_param.sched_priority = rr_max_prio;
   *  pthread_attr_setschedparam( &(this->tattr), &rr_param );
   *  
   *  #else
   */

  this->usecDelay = 0;

  /*  #endif */
}

/*
 *----------------------------------------------------------------------
 *
 * NormalPriority -- Set the normal operating priority of the indexing
 *                   thread.
 *
 * Algorithm: If (POSIX) thread scheduling is present, use the round-robin
 *            scheduler for this thread, and give it 1/4 of the maximum
 *            priority. Otherwise, set a usec sleep delay which will
 *            cause the thread to pause every 100 iterations or so.
 *
 *----------------------------------------------------------------------
 */

void Nlv_ixNormalPriority( T this )
{
  /*  #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
   *
   *  int rr_max_prio, rr_min_prio, rr_low_prio;
   *  struct sched_param rr_param;
   * rr_min_prio = sched_get_priority_min( SCHED_RR );
   *  rr_max_prio = sched_get_priority_max( SCHED_RR );
   * rr_low_prio = rr_min_prio;
   * rr_param.sched_priority = rr_low_prio;
   * pthread_attr_setschedparam( &(this->tattr), &rr_param );
   * 
   * #else
   */
  memset( &(this->tattr), 0, sizeof(this->tattr) );
  this->usecDelay = NLV_INDEX_USLEEP;

  /* #endif */
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateDensity -- Add 1 to density count
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
void Nlv_ixUpdateDensity( T this) 
{
  this->d.cnt++;
}

/*
 *----------------------------------------------------------------------
 *
 * AddToDensity -- ?? If the density bar needs to be added
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
int Nlv_ixAddToDensity( T this,
			double tm )
{
  if ( tm - this->d.tlast > this->d.ival )
    return 1;
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * ResetDensity -- Set density count back to 0, tlast to input
 *
 * Algorithm: Use cntMutex to guarantee thread-safety.
 *
 *----------------------------------------------------------------------
 */
void Nlv_ixResetDensity( T this,
			 double new_tlast )
{
  LOCK_MUTEX(&this->cntMutex);

  this->d.cnt   = 0;
  /*  this->d.tlast = new_tlast; */

  UNLOCK_MUTEX(&this->cntMutex);

}

/*
 *----------------------------------------------------------------------
 *
 * EnterDensity -- Add an entry to the "density count" array.
 *
 * Calls    :
 *
 * Algorithm: Resize the array if full; use cntMutex to guarantee
 *            thread-safety.
 *
 * Side effects: - Increment the last-time variable by
 *               the time interval spanned by each bar.
 *               - If we have to do thread priorities by hand,
 *               this is where we will sleep for the predefined
 *               number of usecs.
 *
 *----------------------------------------------------------------------
 */
void Nlv_ixEnterDensity( T this )
{
  densityData data;
  int         n;

  LOCK_MUTEX(&this->cntMutex);

  if ( this->d.num == this->d.sz ) {
    this->d.sz *= 2;
    this->d.arr = realloc(this->d.arr, this->d.sz * sizeof( densityData ) );
    if ( this->d.arr == NULL ) {
      NLV_FATAL_S( "Unable to reallocate density array. Out of memory?");
      exit(1);
    }
  }
  n = this->d.num++;
  this->d.arr[n].count = this->d.cnt;
  this->d.arr[n].time  = this->d.tlast;
  this->d.tlast       += this->d.ival;
  if ( this->d.cnt > this->d.hcnt ) {
    this->d.hcnt = this->d.cnt;
  }

  UNLOCK_MUTEX(&this->cntMutex);
}

/*
 *----------------------------------------------------------------------
 *
 * DGetHighest -- Return highest count so far.
 *
 * Algorithm: Accessor
 *
 *----------------------------------------------------------------------
 */
long Nlv_ixDGetHighest( T this )
{
  return this->d.hcnt;
}

/*
 *----------------------------------------------------------------------
 *
 * DTimeAtIndex -- Given an index into the array, return the
 *                 relative time, or -1 if out of bounds.
 *
 * Algorithm: Accessor
 *
 *----------------------------------------------------------------------
 */
double Nlv_ixDTimeAtIndex( T this, int index )
{
  if ( index < 0 || index >= this->d.num )
    return -1.;
  return this->d.arr[index].time;
}

/*
 *----------------------------------------------------------------------
 *
 * DIndexAtTime -- Given a time, scan the array and return the index of
 *                 the first array element which has a time greater than
 *                 or equal to the input, or -1 if none.
 *
 * Algorithm: Linear search
 *
 *----------------------------------------------------------------------
 */
int Nlv_ixDIndexAtTime( T this, double reltime )
{
  int i;

  for( i=0; i < this->d.num ; i++ ) {
    if ( this->d.arr[i].time >= reltime )
      return i;
  }
  return -1.;
}

/*
 *----------------------------------------------------------------------
 *
 * DGetCount -- Get density count at index given.
 *
 * Algorithm: If out-of-range, return 0
 *
 *----------------------------------------------------------------------
 */
long Nlv_ixDGetCount( T this, int index )
{
  if ( index >= this->d.num )
    return 0.;
  return this->d.arr[index].count;
}

/*
 *----------------------------------------------------------------------
 *
 * DGetTime -- Get density time at index given.
 *
 * Algorithm: If out-of-range, return 0
 *
 *----------------------------------------------------------------------
 */
long Nlv_ixDGetTime( T this, int index )
{
  if ( index >= this->d.num )
    return 0.;
  return this->d.arr[index].time;
}

/*
 *----------------------------------------------------------------------
 *
 * DGetSize -- Get number of entries in density array which are
 *             before the given time.
 *
 * Algorithm: Accessor.
 *
 *----------------------------------------------------------------------
 */
int Nlv_ixDGetSize( T this,
		    double reltime )
{
  int i;
  for ( i=this->d.num-1; i > 0 && (this->d.arr[i].time > reltime); i-- )
    ;
  return i;
}

/*
 *----------------------------------------------------------------------
 *
 * DSetWinHighest -- Set the 'hwcnt' variable to the highest value in a
 *                   given time window.
 *
 * Algorithm: Examine every element between 2 given times.
 *
 *----------------------------------------------------------------------
 */
void Nlv_ixDSetWinHighest ( T this, double t1, double t2 )
{
  int i; /* array index */

  this->d.hwcnt = 0;
  for ( i= 0; (i < this->d.num) && (this->d.arr[i].time < t1) ; i++ );
  for( ; (i < this->d.num) && (this->d.arr[i].time < t2) ; i++ )
    if ( this->d.arr[i].count > this->d.hwcnt )
      this->d.hwcnt = this->d.arr[i].count;
}

/*
 *----------------------------------------------------------------------
 *
 * DGetWinHighest -- Get the highest value in a given time window.
 *
 * Algorithm: Accessor.
 *
 *----------------------------------------------------------------------
 */
long Nlv_ixDGetWinHighest( T this )
{
  return this->d.hwcnt;
}

double Nlv_ixGetJumpTime( T this ) { return this->jumpTime; }
void Nlv_ixClearJumpTime( T this ) { this->jumpTime = 0.; }

#undef T
#undef FILE_ERR_CODE
