/* queue.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 <stdio.h>
#include <unistd.h>
#include <string.h>
#include "analysis.h"
#include "globals.h"
#include "misc.h"
#include "queue.h"
#include "sync.h"
#include "work.h"
#include "memory.h"
#include "graphics.h"

#define QSIZE 3000


struct entry {
   Context ctx;                   /* the vis5d context */
   Irregular_Context itx;         
   int type;                      /* the type of entry */
   int i1, i2, i3;                /* integer arguments */
   float f1, f2, f3, f4, f5;      /* float arguments */
};



static struct entry queue[QSIZE];
static int qsize;
static int qhead, qtail;      /* remove from head, add to tail */
static int qwaiters;

#ifdef SEMAPHORE
static LOCK qlock;
static SEMAPHORE qnotempty;
#endif



void init_queue( void )
{
   ALLOC_LOCK( qlock );
   ALLOC_SEM( qnotempty, 0 );
   qsize = 0;
   qhead = qtail = 0;
   qwaiters = 0;
}



void terminate_queue( void )
{
   FREE_LOCK( qlock );
   FREE_SEM( qnotempty );
}



/*
 * Return number of entries still in the queue and the number of threads
 * waiting for work to do.
 * If size==0 and waiters==NumWorkThreads then we're idle.
 */
void get_queue_info( int *size, int *waiters )
{
   LOCK_ON( qlock );
   *size = qsize;
   *waiters = qwaiters;
   LOCK_OFF( qlock );
}




static void add_qentry( Context ctx, Irregular_Context itx, 
                        int urgent, int type,
                        int i1, int i2, int i3,
                        float f1, float f2, float f3, float f4, float f5 )
{
   int pos, i, found=0;

   LOCK_ON( qlock );
   while (qsize==QSIZE-2) {
      if (Debug)
         printf("QUEUE FULL!!!\n");
      LOCK_OFF( qlock );
/* WLH 6 Nov 98
      sleep(1);
*/
      /* WLH 6 Nov 98 */
      if (NumThreads==1) {
        do_one_task( 0 );
      }
      else {
        sleep(1);  /* wait a second for the queue to empty a bit */
      }

      LOCK_ON( qlock );
   }

   /* check if already in the queue */
   pos = qhead;
   found = 0;

   for (i=0;i<qsize;i++) {
      /* check if request is already in the queue */
      if (ctx && queue[pos].ctx==ctx &&
          type!=TASK_TRAJ &&
          queue[pos].type==type &&     /* only need to test */
          queue[pos].i1==i1 &&         /* the first four fields */
          queue[pos].i2==i2) {
         /* already in queue, cancel it if urgent */
         found = 1;
         if (urgent)
           queue[pos].type = TASK_NULL;
         break;
      }
      else if (itx && queue[pos].ctx==ctx &&
          queue[pos].type==type &&     /* only need to test */
          queue[pos].i1==i1 &&         /* the first four fields */
          queue[pos].i2==i2) {
         /* already in queue, cancel it if urgent */
         found = 1;
         if (urgent)
           queue[pos].type = TASK_NULL;
         break;
      }
      else {
         pos++;
         if (pos==QSIZE)
           pos = 0;
      }
   }

   if (urgent) {
      /* insert at head */
      if (qhead==0)
         qhead = QSIZE-1;
      else
         qhead--;
      pos = qhead;
      qsize++;
      SIGNAL_SEM( qnotempty );
   }
   else if (!found) {
      /* insert at tail */
      pos = qtail;
      qtail++;
      if (qtail==QSIZE)
        qtail = 0;
      qsize++;
      SIGNAL_SEM( qnotempty );
   }

   queue[pos].ctx = ctx;
   queue[pos].itx = itx;
   queue[pos].type = type;
   queue[pos].i1 = i1;
   queue[pos].i2 = i2;
   queue[pos].i3 = i3;
   queue[pos].f1 = f1;
   queue[pos].f2 = f2;
   queue[pos].f3 = f3;
   queue[pos].f4 = f4;
   queue[pos].f5 = f5;

   if (Debug) { 
      if (urgent)
        printf("**URGENT** **URGENT** **URGENT** **URGENT** ");
      printf("ADDED AT POS=%d\n", pos );
   } 

   LOCK_OFF( qlock );
}



/*** get_qentry *******************************************************
   Return an entry from the work queue.  If the queue is empty, this
   fuction blocks.
**********************************************************************/
void get_qentry( Context *ctx, Irregular_Context *itx,
                 int *type,
                 int *i1, int *i2, int *i3,
                 float *f1, float *f2, float *f3, float *f4, float *f5 )
{
   if (Debug) printf("get_qentry\n");

   LOCK_ON( qlock );

   while (qsize==0) {
      qwaiters++;
      LOCK_OFF( qlock );
      WAIT_SEM( qnotempty );
      LOCK_ON( qlock );
      qwaiters--;
   }


   if (qsize>0) {
      /* remove from head */
      *ctx = queue[qhead].ctx;
      *itx = queue[qhead].itx;
      *type = queue[qhead].type;
      *i1 = queue[qhead].i1;
      *i2 = queue[qhead].i2;
      *i3 = queue[qhead].i3;
      *f1 = queue[qhead].f1;
      *f2 = queue[qhead].f2;
      *f3 = queue[qhead].f3;
      *f4 = queue[qhead].f4;
      *f5 = queue[qhead].f5;

      if (Debug) printf("REMOVED FROM POS=%d\n", qhead );

      if (*type!=TASK_QUIT) {
         qhead++;
         if (qhead==QSIZE)
           qhead = 0;

         qsize--;
      }

   }
   else {
      *type = TASK_NULL;
   }
   LOCK_OFF( qlock );
   if (Debug) printf("return\n");
}



/*** request_quit *****************************************************
   This is called when we want to exit the program.  It puts a message
   on the queue that all 'work' threads are to exit.
**********************************************************************/
void request_quit( Context ctx )
{
   add_qentry( ctx, NULL, 1, TASK_QUIT, 0,0,0, 0.0,0.0,0.0,0.0,0.0 );
}



/*
 * Request that an isosurface is to be computed.
 * Input:  ctx - the context
 *         time - the timestep number
 *         var - which variable
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_isosurface( Context ctx, int time, int var, int urgent )
{

  if(!ctx->Variable[var]->SurfTable[time]){
	 ctx->Variable[var]->SurfTable[time] = (struct isosurface *) allocate(ctx,sizeof(struct isosurface));
	 memset(ctx->Variable[var]->SurfTable[time], 0, sizeof(struct isosurface));
  }

  if (ctx->Variable[var]->SurfTable[time]->valid==0 ||
		ctx->Variable[var]->SurfTable[time]->isolevel!=ctx->IsoLevel[var] ||
		ctx->Variable[var]->SurfTable[time]->colorvar!=ctx->IsoColorVar[var]) {
	 add_qentry( ctx, NULL, urgent, TASK_ISOSURFACE, time, var, 0,
					 0.0, 0.0, 0.0, 0.0, 0.0 );
  }
}


void request_hclip( Context ctx, int num)
{
   add_qentry( ctx, NULL, 1, TASK_HCLIP, num, 0, 0, 
                  0.0, 0.0, 0.0, 0.0, 0.0 );
}

void request_vclip( Context ctx, int num)
{
   add_qentry( ctx, NULL, 1, TASK_VCLIP, num, 0, 0,
                  0.0, 0.0, 0.0, 0.0, 0.0 );
}

#ifdef HAVE_LIBNETCDF
void request_text_plot( Irregular_Context itx, int time, int var, int urgent)
{
   if (itx->TextPlotTable[time].valid == 0 ||
       itx->TextPlotTable[time].spacing != itx->TextPlotSpacing ||
       itx->TextPlotTable[time].fontx != itx->TextPlotFontX ||
       itx->TextPlotTable[time].fonty != itx->TextPlotFontY ||
       itx->TextPlotTable[time].fontspace != itx->TextPlotFontSpace ){
      add_qentry( NULL, itx, urgent, TASK_TEXT_PLOT, time, var, 
      0, 0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}
#endif /* HAVE_LIBNETCDF */



/*
 * Request that a horizontal contour line slice is to be computed.
 * Input:  ctx - the context
 *         time - the timestep number
 *         var - which variable
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_hslice( Context ctx, int time, int var, int urgent )
{
  if(! ctx->Variable[var]->HSliceTable[time]){
	 ctx->Variable[var]->HSliceTable[time] = (struct hslice *) allocate(ctx, sizeof(struct hslice));
	 memset(ctx->Variable[var]->HSliceTable[time], 0, sizeof(struct hslice));
  }
  if (ctx->Variable[var]->HSliceTable[time]->valid==0 ||
		ctx->Variable[var]->HSliceTable[time]->level!=ctx->Variable[var]->HSliceRequest->Level ||
		ctx->Variable[var]->HSliceTable[time]->interval!=ctx->Variable[var]->HSliceRequest->Interval ||
		ctx->Variable[var]->HSliceTable[time]->lowlimit!=ctx->Variable[var]->HSliceRequest->LowLimit ||
		ctx->Variable[var]->HSliceTable[time]->highlimit!=ctx->Variable[var]->HSliceRequest->HighLimit ) {
	 add_qentry( ctx, NULL, urgent, TASK_HSLICE, time, var, 0,
					 0.0, 0.0, 0.0, 0.0, 0.0 );
  }
}



/*
 * Request that a vertical contour line slice is to be computed.
 * Input:  ctx - the context
 *         time - the timestep number
 *         var - which variable
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_vslice( Context ctx, int time, int var, int urgent )
{
  if(! ctx->Variable[var]->VSliceTable[time]){
	 ctx->Variable[var]->VSliceTable[time] = (struct vslice *) allocate(ctx, sizeof(struct vslice));
	 memset(ctx->Variable[var]->VSliceTable[time], 0, sizeof(struct vslice));
  }

  if (! ctx->Variable[var]->VSliceTable[time] ||
		ctx->Variable[var]->VSliceTable[time]->valid==0 ||
		ctx->Variable[var]->VSliceTable[time]->r1!=ctx->Variable[var]->VSliceRequest->R1 ||
		ctx->Variable[var]->VSliceTable[time]->c1!=ctx->Variable[var]->VSliceRequest->R2 ||
		ctx->Variable[var]->VSliceTable[time]->r2!=ctx->Variable[var]->VSliceRequest->C1 ||
		ctx->Variable[var]->VSliceTable[time]->c2!=ctx->Variable[var]->VSliceRequest->C2 ||
		ctx->Variable[var]->VSliceTable[time]->interval!=ctx->Variable[var]->VSliceRequest->Interval ||
		ctx->Variable[var]->VSliceTable[time]->lowlimit!=ctx->Variable[var]->VSliceRequest->LowLimit ||
		ctx->Variable[var]->VSliceTable[time]->highlimit!=ctx->Variable[var]->VSliceRequest->HighLimit ) {
	 add_qentry( ctx, NULL, urgent, TASK_VSLICE, time, var, 0,
					 0.0, 0.0, 0.0, 0.0, 0.0 );
  }
}



/*
 * Request that a horizontal color slice is to be computed.
 * Input:  ctx - the context
 *         time - the timestep number
 *         var - which variable
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_chslice( Context ctx, int time, int var, int urgent )
{
  if(! ctx->Variable[var]->CHSliceTable[time]){
	 ctx->Variable[var]->CHSliceTable[time] = (struct chslice *) allocate(ctx, sizeof(struct chslice));
	 memset(ctx->Variable[var]->CHSliceTable[time], 0, sizeof(struct chslice));
  }

  if (ctx->Variable[var]->CHSliceTable[time]->valid==0 ||
		ctx->Variable[var]->CHSliceTable[time]->level!=ctx->Variable[var]->CHSliceRequest->Level ) {
	 add_qentry( ctx, NULL, urgent, TASK_CHSLICE, time, var, 0,
					 0.0, 0.0, 0.0, 0.0, 0.0 );
  }
}



/*
 * Request that a vertical colored slice is to be computed.
 * Input:  ctx - the context
 *         time - the timestep number
 *         var - which variable
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_cvslice( Context ctx, int time, int var, int urgent )
{
  if(! ctx->Variable[var]->CVSliceTable[time]){
	 ctx->Variable[var]->CVSliceTable[time] = (struct cvslice *) allocate(ctx, sizeof(struct cvslice));
	 memset(ctx->Variable[var]->CVSliceTable[time], 0, sizeof(struct cvslice));
  }

  if (ctx->Variable[var]->CVSliceTable[time]->valid==0 ||
		ctx->Variable[var]->CVSliceTable[time]->r1!=ctx->Variable[var]->CVSliceRequest->R1 ||
		ctx->Variable[var]->CVSliceTable[time]->c1!=ctx->Variable[var]->CVSliceRequest->R2 ||
		ctx->Variable[var]->CVSliceTable[time]->r2!=ctx->Variable[var]->CVSliceRequest->C1 ||
		ctx->Variable[var]->CVSliceTable[time]->c2!=ctx->Variable[var]->CVSliceRequest->C2 ) {
	 add_qentry( ctx, NULL, urgent, TASK_CVSLICE, time, var, 0,
					 0.0, 0.0, 0.0, 0.0, 0.0 );
  }
}



/*
 * Request that a horizontal wind vector slice is to be computed.
 * Input:  dtx - the display context
 *         time - the display timestep number
 *         ws - which wind slice
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_hwindslice( Display_Context dtx, int time, int ws, int urgent )
{
   Context ctx;

   ctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->HWindTable[ws][time].valid==0 ||
       dtx->HWindTable[ws][time].uvar!=dtx->Uvar[ws] ||
       dtx->HWindTable[ws][time].vvar!=dtx->Vvar[ws] ||
       dtx->HWindTable[ws][time].wvar!=dtx->Wvar[ws] ||
       dtx->HWindTable[ws][time].uvarowner!=dtx->Uvarowner[ws] ||
       dtx->HWindTable[ws][time].vvarowner!=dtx->Vvarowner[ws] ||
       dtx->HWindTable[ws][time].wvarowner!=dtx->Wvarowner[ws] ||
       dtx->HWindTable[ws][time].level!=dtx->HWindLevel[ws] ||
       dtx->HWindTable[ws][time].scale!=dtx->HWindScale[ws] ||
       dtx->HWindTable[ws][time].density!=dtx->HWindDensity[ws] ||
       dtx->HWindTable[ws][time].barbs!=dtx->WindBarbs ) {

       /* use the first ctx in the dtx to pass to add_qentry */ 
       add_qentry( ctx, NULL, urgent,
                   TASK_HWIND, time, ws,0 ,
                   0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}



/*
 * Request that a vertical wind vector slice is to be computed.
 * Input:  ctx - the display context
 *         time - the timestep number
 *         ws - which wind slice
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_vwindslice(Display_Context dtx, int time, int ws, int urgent )
{
   Context ctx;

   ctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->VWindTable[ws][time].valid==0 ||
       dtx->VWindTable[ws][time].uvar!=dtx->Uvar[ws] ||
       dtx->VWindTable[ws][time].vvar!=dtx->Vvar[ws] ||
       dtx->VWindTable[ws][time].wvar!=dtx->Wvar[ws] ||
       dtx->VWindTable[ws][time].uvarowner!=dtx->Uvarowner[ws] ||
       dtx->VWindTable[ws][time].vvarowner!=dtx->Vvarowner[ws] ||
       dtx->VWindTable[ws][time].wvarowner!=dtx->Wvarowner[ws] ||
       dtx->VWindTable[ws][time].r1!=dtx->VWindR1[ws] ||
       dtx->VWindTable[ws][time].c1!=dtx->VWindC1[ws] ||
       dtx->VWindTable[ws][time].r2!=dtx->VWindR2[ws] ||
       dtx->VWindTable[ws][time].c2!=dtx->VWindC2[ws] ||
       dtx->VWindTable[ws][time].scale!=dtx->VWindScale[ws] ||
       dtx->VWindTable[ws][time].density!=dtx->VWindDensity[ws] ||
       dtx->VWindTable[ws][time].barbs!=dtx->WindBarbs ) {
      add_qentry( ctx, NULL,
                  urgent, TASK_VWIND, time, ws, 0,
                 0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}



/*
 * Request that a horizontal streamline slice is to be computed.
 * Input:  ctx - the display context
 *         time - the timestep number
 *         ws - which slice
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_hstreamslice(Display_Context dtx, int time, int ws, int urgent )
{
   Context ctx;

   ctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->HStreamTable[ws][time].valid==0 ||
       dtx->HStreamTable[ws][time].uvar!=dtx->Uvar[ws] ||
       dtx->HStreamTable[ws][time].vvar!=dtx->Vvar[ws] ||
       dtx->HStreamTable[ws][time].uvarowner!=dtx->Uvarowner[ws] ||
       dtx->HStreamTable[ws][time].vvarowner!=dtx->Vvarowner[ws] ||
       dtx->HStreamTable[ws][time].level!=dtx->HStreamLevel[ws] ||
       dtx->HStreamTable[ws][time].density!=dtx->HStreamDensity[ws] ) {
      add_qentry( ctx, NULL,
                  urgent, TASK_HSTREAM, time, ws, 0,
                  0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}



/*
 * Request that a vertical streamline slice is to be computed.
 * Input:  ctx - the display context
 *         time - the timestep number
 *         ws - which slice
 *         urgent - 1 = put request at front of queue, 0 = put at back of queue
 */
void request_vstreamslice(Display_Context dtx, int time, int ws, int urgent )
{
   Context ctx;

   ctx = dtx->ctxpointerarray[return_ctx_index_pos(dtx, dtx->Uvarowner[ws])];
   if (dtx->VStreamTable[ws][time].valid==0 ||
       dtx->VStreamTable[ws][time].uvar!=dtx->Uvar[ws] ||
       dtx->VStreamTable[ws][time].vvar!=dtx->Vvar[ws] ||
       dtx->VStreamTable[ws][time].wvar!=dtx->Wvar[ws] ||
       dtx->VStreamTable[ws][time].uvarowner!=dtx->Uvarowner[ws] ||
       dtx->VStreamTable[ws][time].vvarowner!=dtx->Vvarowner[ws] ||
       dtx->VStreamTable[ws][time].wvarowner!=dtx->Wvarowner[ws] ||
       dtx->VStreamTable[ws][time].r1!=dtx->VStreamR1[ws] ||
       dtx->VStreamTable[ws][time].c1!=dtx->VStreamC1[ws] ||
       dtx->VStreamTable[ws][time].r2!=dtx->VStreamR2[ws] ||
       dtx->VStreamTable[ws][time].c2!=dtx->VStreamC2[ws] ||
       dtx->VStreamTable[ws][time].density!=dtx->VStreamDensity[ws] ) {
      add_qentry( ctx, NULL,
                  urgent, TASK_VSTREAM, time, ws, 0,
                  0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}




/*** request_traj *****************************************************
   This is called to compute a new trajectory.
   Input:  row, col, lev - start pos in [0..Nr-1],[0..Nc-1],[0..Nl-1].
           time - start time in [0..NumTimes-1].
           group - which traj group in [0..TRAJGROUPS-1].
           rib - non-zero means make ribbon instead of line traj.
           step - integration step multiplier.
           len - trajectory length multiplier.
**********************************************************************/
void request_traj( Display_Context dtx, float row, float col, float lev,
                   int time, int group, int rib,
                   float step, float len )
{
   add_qentry( dtx->ctxpointerarray[0], NULL, 1, TASK_TRAJ,
               time, group, rib, row, col, lev, step, len );
}



/*
 * Request that all trajectories in a group be recolored according to
 * the current TrajColorVar variable.
 */
void request_traj_recoloring( Context ctx, int traj_set )
{
   add_qentry( ctx, NULL, 1, TASK_TRAJ_RECOLOR,
               traj_set, 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0 );
}




/*
 * Put a job into the queue for computing an external function
 */
void request_ext_func( Context ctx, int time, int var )
{
#ifdef SINGLE_TASK
   calc_ext_func( ctx, time, var, 1 );
#else
   add_qentry( ctx, NULL, 0, TASK_EXT_FUNC, time, var, 0,
               0.0, 0.0, 0.0, 0.0, 0.0 );
#endif
}



/*
 * Request that the topography be recolored according to a 2-D variable
 * or according to height.
 */
void request_topo_recoloring( Context ctx )
{
   int time, urgent;

   for (time=0;time<ctx->dpy_ctx->NumTimes;time++) {
      if (time==ctx->dpy_ctx->CurTime) {
         urgent = 1;
      }
      else {
         urgent = 0;
      }
      add_qentry( ctx, NULL, urgent, TASK_TOPO_RECOLOR,
                  time, 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0 );
   }
}




syntax highlighted by Code2HTML, v. 0.9.1