/* record.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"

#ifdef HAVE_LIBNETCDF
 
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "api.h"
#include "binio.h"
#include "record.h"
#include "globals.h"
#include "memory.h"
#include "imemory.h"
#include "proj.h"
#include "sync.h"
#include "irregular_v5d.h"
#include "v5d.h"
 
 
#ifndef M_PI
#  define M_PI 3.14159265
#endif
 
#define DEG2RAD (M_PI/180.0)
#define RAD2DEG (180.0/M_PI)
 


int init_record_cache( Irregular_Context itx, int maxbytes, float *ratio )
{
   int i, j;
   int numfloatvars, numcharvars, numberofchars; 
   int recordsize;
   int soundingsize;
   int totalnumrecs;

   /* dyamically allocate memory for RecordTable since */
   /* MAXTIMES*MAXRECS is way too large                */
   for (i = 0; i < itx->NumTimes; i++){
      itx->RecordTable[i] = (struct irreg_rec *) calloc(itx->NumRecs[i], sizeof(struct irreg_rec));
   }

   ALLOC_LOCK(itx->Mutex);

   /*********************************/
   /* figure out size of one record */
   /*********************************/
   numfloatvars = 0;
   numcharvars = 0;
   numberofchars = 0;
   soundingsize = 0;
   for (i = 0; i < itx->NumVars; i++){
      if (itx->Variable[i]->VarType == NUMERICAL_VAR_1D ){
         numfloatvars++;
      }
      else if (itx->Variable[i]->VarType == NUMERICAL_VAR_2D ){
         soundingsize += itx->Levels;
      }
      else if (itx->Variable[i]->VarType == CHARACTER_VAR){
         numberofchars += itx->Variable[i]->CharVarLength;
         numcharvars++;
      }
      else{
         printf("Error in init_record_cache\n");
         return -1;
      }
   }
   if (itx->Type == SOUNDING){
      recordsize = numfloatvars * sizeof(double) +
                   soundingsize * sizeof(double) +
                   itx->Levels  * sizeof(float)  +
                   numberofchars  * sizeof(char);
   }
   else{
      recordsize = numfloatvars * sizeof(double) +
                   soundingsize * sizeof(double) +
                   numberofchars  * sizeof(char);
   }
   itx->CharArrayLength = numberofchars;


   /****************************************************/   
   /* figure out how many records can be put in memory */
   /****************************************************/
   itx->MaxCachedRecs = (int) maxbytes / recordsize;


   /**********************/
   /* get total num recs */
   /**********************/
   totalnumrecs = 0;
   for (i = 0; i < itx->NumTimes; i++){
      totalnumrecs += itx->NumRecs[i];
   }

   if (itx->MaxCachedRecs >= totalnumrecs){
      /* all records can be cached */
      itx->MaxCachedRecs = totalnumrecs;
      printf("Reading all records\n");
      *ratio = 1.0;
   }
   else{
      *ratio = ((float) itx->MaxCachedRecs)
             / ((float) totalnumrecs);
   }

   itx->NumCachedRecs = 0;

   printf("Cache size: %d records\n", itx->MaxCachedRecs);

   /****************************/
   /* allocate irregular cache */
   /****************************/
   itx->RecordCache = (struct cache_irreg_rec *) i_allocate( itx, itx->MaxCachedRecs
                                                 * sizeof(struct cache_irreg_rec) ); 

   /************/   
   /* check it */
   /************/
   if (!itx->RecordCache){
      printf("Error1: out of memory.  Couldn't allocate cache space.\n");
      return 0;
   }

   
   /******************************************************************/
   /* allocate memory for RecGeoPosition, this simply contains the   */
   /* lat, lon, alt for all the records.  This was removed from the  */
   /* cache_irreg_rec becuase if all the records can not fit into    */
   /* the cache, they will have to be loaded at some point in time   */
   /* into the cache in order to see whether or not to be used in a  */
   /* graphic, by loading all the lat, lon, and alts and keeping them*/
   /* this will eliminate this problem and the record cache will be  */
   /* more effective                                                 */
   /******************************************************************/

   for (i = 0; i < itx->NumTimes; i++){
      itx->RecGeoPosition[i] = (struct rec_geo_position *) i_allocate( itx,
                                                   itx->NumRecs[i] *
                                                   sizeof(struct rec_geo_position) );
      if (!itx->RecGeoPosition[i]){
         printf("Not enough memory to allocate for RecGeoPosition\n");
         return 0;
      }
   }


   itx->CacheClock = 1;

   for (i=0;i<itx->MaxCachedRecs;i++){
      /************************/
      /* alloc DataType array */
      /************************/
      itx->RecordCache[i].DataType =
                            i_allocate( itx, itx->NumVars*sizeof(int));
      /************/            
      /* check it */
      /************/
      if (!itx->RecordCache[i].DataType){
         printf("Error3: out of memory.  Couldn't allocate cache space.\n");
         return 0;
      }

      /***************/
      /* alloc Value */
      /***************/
      itx->RecordCache[i].Value =
                            i_allocate( itx, itx->NumVars*sizeof(double));
      /************/                        
      /* check it */
      /************/            
      if (!itx->RecordCache[i].Value){
         printf("Error4: out of memory.  Couldn't allocate cache space.\n");
         return 0;
      }

      /*************************/
      /* alloc Sounding Values */
      /*************************/
      if (soundingsize){
         itx->RecordCache[i].SoundingValue =
                           i_allocate( itx, soundingsize * sizeof(double));
         /************/
         /* check it */
         /************/
         if (!itx->RecordCache[i].SoundingValue){
            printf("Error5: out of memory.  Couldn't allocate cache space.\n");
            return 0;
         }
         itx->RecordCache[i].SoundingLevel = 
                           i_allocate( itx, itx->Levels * sizeof(float));
         if(!itx->RecordCache[i].SoundingLevel){
            printf("Error6: out of memory.  Couldn't allocate cache space.\n");
            return 0;
         }
      }

      /***********************/            
      /* Alloc char var data */
      /***********************/
      itx->RecordCache[i].CharData =
                            i_allocate( itx, numberofchars*sizeof(char ));

      /************/                                    
      /* check it */            
      /************/                        
      if (!itx->RecordCache[i].CharData){
         printf("Error7: out of memory.  Couldn't allocate cache space.\n");
         return 0;
      }


      itx->RecordCache[i].Locked = 0;
      itx->RecordCache[i].Timestep = 0;
   }


   for (i=0; i<itx->NumTimes; i++){
      for (j = 0; j < itx->NumRecs[i]; j++){
         itx->RecordTable[i][j].CachePos = -1;
         itx->RecordTable[i][j].DataType = NULL;
         itx->RecordTable[i][j].Value = NULL;
         itx->RecordTable[i][j].SoundingValue = NULL;
         itx->RecordTable[i][j].SoundingLevel = NULL;
         itx->RecordTable[i][j].CharData = NULL;
      }
   }
   return 1;
}

int get_empty_irreg_cache_pos(Irregular_Context itx)
{
   int g;

   /* fing g */
   if (itx->NumCachedRecs < itx->MaxCachedRecs ){
      /* There's an unused position. */
      g = itx->NumCachedRecs;
      itx->NumCachedRecs++;
   }
   else{
      int time, rec, minage, i, mini;
      minage = itx->CacheClock;
      for (i=0;i<itx->MaxCachedRecs;i++) {
         if (itx->RecordCache[i].Age<minage && itx->RecordCache[i].Locked==0) {
            minage = itx->RecordCache[i].Age;
            mini = i;
         }
      }
      g = mini;
      /* remove references to data being discarded */
      time = itx->RecordCache[g].Timestep;
      rec = itx->RecordCache[g].Rec;
      itx->RecordTable[time][rec].DataType = NULL;
      itx->RecordTable[time][rec].Value = NULL;
      itx->RecordTable[time][rec].SoundingValue = NULL;
      itx->RecordTable[time][rec].SoundingLevel = NULL;
      itx->RecordTable[time][rec].CharData = NULL;
      itx->RecordTable[time][rec].CachePos = -1;
   }
   itx->RecordCache[g].Locked = 1;
   return g;
}


static void load_record(Irregular_Context itx, int time, int record)
{
   int p;

   LOCK_ON( itx->Mutex );

   if (itx->RecordTable[time][record].CachePos > 0){
      /* already in the cache */
      p = itx->RecordTable[time][record].CachePos;
      if (p >= 0){
         itx->RecordCache[p].Locked = 1;
         itx->RecordCache[p].Age = itx->CacheClock++;
         itx->RecordCache[p].Locked = 0;
      }
      LOCK_OFF( itx->Mutex );
      return;
   }
   else{
      /* not in cache */
      int g;
      g = get_empty_irreg_cache_pos(itx);
      if (!irregular_v5dReadRecord( &itx->G, time, record,
          itx->RecordCache[g].Value,
          itx->RecordCache[g].SoundingValue,
          itx->RecordCache[g].CharData,
          itx->RecordCache[g].SoundingLevel)){
         printf("Error: unable to read record information\n");
         LOCK_OFF( itx->Mutex );
         itx->RecordCache[g].Locked = 0;
         return;
      }
      itx->RecordTable[time][record].DataType = itx->RecordCache[g].DataType;
      itx->RecordTable[time][record].Value = itx->RecordCache[g].Value;
      itx->RecordTable[time][record].SoundingValue = itx->RecordCache[g].SoundingValue;
      itx->RecordTable[time][record].SoundingLevel = itx->RecordCache[g].SoundingLevel;
      itx->RecordTable[time][record].CharData = itx->RecordCache[g].CharData;

      itx->RecordTable[time][record].CachePos = g;
      itx->RecordCache[g].Locked = 1;
      itx->RecordCache[g].Timestep = time;
      itx->RecordCache[g].Rec = record;
      itx->RecordCache[g].Age = itx->CacheClock++;
      LOCK_OFF(itx->Mutex);
      itx->RecordCache[g].Locked = 0;
      return;
   }
}

void get_all_record_numerical_data( Irregular_Context itx, int time,
                                int var, double *data)
{
   int i;

   if (itx->Variable[var]->VarType != NUMERICAL_VAR_1D){
      printf("wrong var type in get_record_numerical_data\n");
      return;
   }

   for (i = 0; i < itx->NumRecs[time]; i++){
      if (itx->RecordTable[time][i].CachePos < 0){
         load_record(itx, time, i);
      }
      data[i] = itx->RecordTable[time][i].Value[var];
   }
}

void get_some_record_numerical_data( Irregular_Context itx, int time, 
                                int var, int ploton[], double *data)
{
   int i, pcount = 0;

   if (itx->Variable[var]->VarType != NUMERICAL_VAR_1D){
      printf("wrong var type in get_record_numerical_data\n");
      return;
   }

   for (i = 0; i < itx->NumRecs[time]; i++){
      if (ploton[i]){
         if (itx->RecordTable[time][i].CachePos < 0){
            load_record(itx, time, i);
         }   
         data[pcount] = itx->RecordTable[time][i].Value[var];
         pcount++;
      }
   }
}

void get_all_record_char_data( Irregular_Context itx, int time,
                                int var, char *data)
{
   int i, j, count=0;
   
   if (itx->Variable[var]->VarType != CHARACTER_VAR){
      printf("wrong var type in get_record_char_data\n");
      return;
   }

   for (i = 0; i < itx->NumRecs[time]; i++){
      if (itx->RecordTable[time][i].CachePos < 0){
         load_record(itx, time, i);
      }
      for (j = itx->Variable[var]->CharPointer; j < itx->Variable[var]->CharPointer+
                                          itx->Variable[var]->CharVarLength; j++){
         data[count] = itx->RecordTable[time][i].CharData[j];
         count++;
      }
   }
}

            
void get_some_record_char_data( Irregular_Context itx, int time,
                                int var, int ploton[], char *data)
{
   int i, j, count=0;
   
   if (itx->Variable[var]->VarType != CHARACTER_VAR){
      printf("wrong var type in get_record_char_data\n");
      return;
   }

   for (i = 0; i < itx->NumRecs[time]; i++){
      if (ploton[i]){
         if (itx->RecordTable[time][i].CachePos < 0){
            load_record(itx, time, i);
         }
         for (j = itx->Variable[var]->CharPointer; j < itx->Variable[var]->CharPointer+
                                             itx->Variable[var]->CharVarLength; j++){
            data[count] = itx->RecordTable[time][i].CharData[j];
            count++;
         }
      }
   }
}

            

void get_record_location( Irregular_Context itx, int time, int rec,
                           float *lat, float *lon, float *alt)
{
   *lat = itx->RecGeoPosition[time][rec].Latitude;
   *lon = itx->RecGeoPosition[time][rec].Longitude*-1.0;
   *alt = itx->RecGeoPosition[time][rec].Altitude/1000.0;
}

void get_record_locations( Irregular_Context itx, int time, 
                           float *lat, float *lon, float *alt)
{
   int i;
   
   for (i = 0; i < itx->NumRecs[time]; i++){
      lat[i] = itx->RecGeoPosition[time][i].Latitude;
      lon[i] = itx->RecGeoPosition[time][i].Longitude*-1.0;
      alt[i] = itx->RecGeoPosition[time][i].Altitude/1000.0;
   }
}


void load_geo_data( Irregular_Context itx )
{
   int i, j;
   float lat, lon, alt;

   for (i = 0; i < itx->NumTimes; i++){
      for (j = 0; j < itx->NumRecs[i]; j++){
         if (!irregular_v5dReadRecordGeoData( &itx->G, i, j, &lat, &lon, &alt)){
            printf("Error in reading Geo Data\n");
            return;
         }
         itx->RecGeoPosition[i][j].Latitude = lat;
         itx->RecGeoPosition[i][j].Longitude = lon;
         itx->RecGeoPosition[i][j].Altitude = alt;
      }
   }
}


void preload_irregular_cache( Irregular_Context itx )
{
   int numrecs = 0; 
   int time = 0;
   int trec = 0;
	/*
   printf("Loading Records for time %d\n", time);
	*/
   while ( numrecs < itx->MaxCachedRecs){
      if (trec == itx->NumRecs[time]){
         trec = 0;
         time++;
			/*
         printf("Loading Records for time %d\n", time);   
			*/
      }
      load_record(itx, time, trec);
      numrecs++;
      trec++;
   }
}


int initially_open_recordfile( char filename[], irregular_v5dstruct *iv )
{
   char name[1000];

   strcpy( name, filename);

   if (!irregular_v5dOpenFile( name, iv)){
      printf("Error: datafile %s could not be loaded\n", filename);
      return 0;
   }
   return 1;
}




int open_recordfile(Irregular_Context itx, char filename[])
{
   int i, time, first;
 
   if (!initially_open_recordfile( filename, &itx->G)){
      return 0;
   }

  
   /* Copy header info from G to global variables */
   strcpy(itx->DataFile, filename);

   itx->Type = itx->G.Type;
   itx->Levels = itx->G.Levels; 
   itx->NumVars = itx->G.NumVars;
   itx->NumTimes = itx->G.NumTimes;
   itx->TopBound = itx->G.TopBound;
   itx->BottomBound = itx->G.BottomBound;
   itx->WestBound = itx->G.WestBound;
   itx->EastBound = itx->G.EastBound;
   itx->NorthBound = itx->G.NorthBound;
   itx->SouthBound = itx->G.SouthBound;
	
   for (i = 0; i < itx->NumVars; i++){
	  itx->Variable[i] = (vis5d_irregular_variable *) i_allocate(itx,sizeof(vis5d_irregular_variable));
	  strcpy(itx->Variable[i]->VarName, itx->G.VarName[i]);
	  itx->Variable[i]->VarType = itx->G.VarType[i];
	  itx->Variable[i]->CharVarLength = itx->G.CharVarLength[i];
	  itx->Variable[i]->CharPointer = itx->G.CharPointer[i];
	  itx->Variable[i]->SoundingPointer = itx->G.SoundingPointer[i];
	  itx->Variable[i]->MinVal = itx->G.VarMin[i];
	  itx->Variable[i]->MaxVal = itx->G.VarMax[i];

	}
  itx->TopBound = 10.0;
  itx->BottomBound = -0.1;
  if (itx->WestBound == itx->EastBound){
      itx->WestBound += 10.0;
      itx->EastBound -= 10.0;
   }
   if (itx->SouthBound == itx->NorthBound){
      itx->SouthBound -= 10.0;
      itx->NorthBound += 10.0;
   }

   /* do some checking */
   if (itx->NumVars > MAXVARS){
      printf("Error: Too many variables (%d) limit is %d\n",
             itx->NumVars, MAXVARS);
      return 0;
   }
   if (itx->NumTimes >MAXTIMES) {
      printf("Error: Too many time steps (%d) limit is %d\n", 
              itx->NumTimes, MAXTIMES);
      return 0;
   }

   for (time = 0; time < itx->NumTimes; time++){
      itx->TimeStamp[time] = v5dHHMMSStoSeconds( itx->G.TimeStamp[time] );
      itx->DayStamp[time] = v5dYYDDDtoDays( itx->G.DateStamp[time] );
      itx->NumRecs[time] = itx->G.NumRecs[time];
   }

   /* calculate elapsed time (in seconds) for each time since initial time */
   first = itx->DayStamp[0]* 24*60*60 + itx->TimeStamp[0];
   for (time=0;time<itx->NumTimes;time++) {
      itx->Elapsed[time] = itx->DayStamp[time] * 24*60*60
                         + itx->TimeStamp[time] - first;
   }

   return 1;
}

#endif /* HAVE_LIBNETCDF */


syntax highlighted by Code2HTML, v. 0.9.1