/* file.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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "file.h"
#include "igui.h"

#define MAX_FDBS 99

static FileDB *fdb_table = NULL;
static void init_file_db( FileDB fdb);


/***********************************/
/* This will initialize the fdb or */
/* File Data Base                  */
/***********************************/
int fdb_initialize( void )
{
   int i;

	if(fdb_table!=NULL)
	  free(fdb_table);

	fdb_table = (FileDB **) calloc(MAX_FDBS, sizeof(FileDB *));
	/*
   fdb_table = malloc( sizeof(FileDB *) * MAX_FDBS );
   for (i = 0; i < MAX_FDBS; i++){
      fdb_table[i] = NULL;
   }
	*/
   Initialize_NetCDF_Format_Info();

   return 1;
}

/*****************************************/
/* This will return an index to the next */
/* available fdb from the table          */
/*****************************************/
/* Output: returns the index of next file*/
/*         database or -1 if not one     */
/*****************************************/
int alloc_fdb( void )
{
   int i;
  
   for (i = 0; i < MAX_FDBS; i++){
      if (fdb_table[i] == NULL){
         return i;
      }
   }
   return -1;
}



/*********************************************/
/* This will allocate one fdb structure and  */
/* initialize it.  It will exit if not enough*/
/* memory availabe.                          */
/*********************************************/
/* Ouput: return pointer to a fdb structure  */
/*********************************************/
FileDB new_fdb( void )
{
   FileDB fdb;
   fdb = (FileDB) calloc( 1, sizeof(struct file_db) );
   if (!fdb){
      printf("error in new_fdb\n");
      exit(0);
   }
   else{
      init_file_db( fdb );
   }
   return fdb;
}



/**********************************************/
/* This will return an iniialized and indexed */
/* fdb(file database) ready to use.           */
/**********************************************/
/* Output: return pointer to fdb, this is     */
/*         called from 'imain.c'              */
/**********************************************/
FileDB make_new_fdb( void )
{
   int index;
   FileDB fdb;

   index = alloc_fdb();
   if (index < 0){
      printf("Could not make new fdb\n");
      exit(0);
   }
   fdb = fdb_table[index] = new_fdb();
   fdb->index = index;
   return fdb;
}



/*****************************************************/
/* This will return the desired fdb, or file database*/
/*****************************************************/
/* Input: index - index to desired fdb               */
/* Output: return pointer to fdb                     */
/*****************************************************/
FileDB get_fdb( int index )
{
   if (index<0 || index >= MAX_FDBS){
      printf("error in get_fdb\n");
      exit(0);
   }
   else{
      return fdb_table[index];
   }
}


   
/*************************************************/
/* allocate the file_info structure, exit if not */
/* enough memory                                 */
/*************************************************/
struct file_info *alloc_file_info( void )
{
   struct file_info *yo;

   yo = (struct file_info *) calloc(1, sizeof(struct file_info));
   if (!yo){
      printf(" could not allocate file_info\n");
      exit(0);
   }
   return yo;
}



/*******************************************/
/* Free the file info and free the RecID's */
/*******************************************/
/* Input: finfo - pointer to the file_info */
/*                struct                   */
/*******************************************/
void free_file_info( struct file_info *finfo)
{
   int i;
   for (i = 0; i < finfo->NumTimes; i++){
      free(finfo->RecID[i]);
   }
   free( finfo); 
}



/**********************************/
/* Init certain values of the fdb */
/**********************************/
/* Input: fdb - pointer to fdb    */
/**********************************/
static void init_file_db( FileDB fdb)
{
   fdb->NumFiles = 0;
   fdb->NumTimes = 0;
   fdb->NumVars  = 0;
   fdb->Levels   = 0;
   fdb->WhichFileOpen = -1;
}


/**************************************************/
/* Remove a file from a fdb and do the required   */
/* functions such as re-determining the variables */
/* or getting new boundries                       */
/**************************************************/
/* Input: fdb - pointer to the fdb                */
/*        file_index - file index indicating which*/
/*                     file to be removed         */
/**************************************************/
int remove_a_file( FileDB fdb, int file_index)
{
   struct file_info *f;
   int i, j, k, tt, td, save_it, throw;

   f = fdb->File[file_index];

   /*****************************************/
   /* get the appropriate time removed also */
   /*****************************************/
   /*******************************************/
   /* first see if it's need, some other file */
   /* might have the same time step           */
   /*******************************************/
   for (i = 0; i < f->NumTimes; i++){
      td = f->DateStamp[i];
      tt = f->TimeStamp[i];
      save_it = 0;
      for (j = 0; j < fdb->NumFiles && !save_it; j++){
         if ( j != file_index ){
            for (k = 0; k < fdb->File[j]->NumTimes && !save_it; k++){
               if ( td == fdb->File[j]->DateStamp[k] &&
                    tt == fdb->File[j]->TimeStamp[k]){
                  save_it = 1;
               }
            }
         }
      }
      /******************************************/
      /* find out which one it is and chuck it! */
      /******************************************/
      throw = -1;
      for (j = 0; j < fdb->NumTimes; j++){
         if (fdb->DateStamp[j] == td &&
             fdb->TimeStamp[j] == tt){
            throw = j;
            j = fdb->NumTimes;
         }
      }
      if (throw == -1){
         /* something majorly wront here */
         exit(0);
      }

      if (!save_it){
         if (throw == fdb->NumTimes-1){
            fdb->DateStamp[throw] = 0;
            fdb->TimeStamp[throw] = 0;
            fdb->TimeSeconds[throw] = 0;
            fdb->NumRecs[throw] = 0;
            fdb->NumTimes--;
         }
         else{
            for (j = throw+1; j < fdb->NumTimes; j++){
               fdb->DateStamp[j-1] = fdb->DateStamp[j];
               fdb->TimeStamp[j-1] = fdb->TimeStamp[j];
               fdb->TimeSeconds[j-1] = fdb->TimeSeconds[j];
               fdb->TimeSelected[j-1] = fdb->TimeSelected[j];
               fdb->NumRecs[j-1] = fdb->NumRecs[j];
            }
            fdb->DateStamp[j-1] = 0;
            fdb->TimeStamp[j-1] = 0;
            fdb->TimeSelected[j-1] = 0;
            fdb->TimeSeconds[j-1] = 0;
            fdb->NumRecs[j-1] = 0;
            fdb->NumTimes--;
         }
      }
      else{
         fdb->NumRecs[throw] -= f->NumRecs[i];
      }
   }
         
   /********************************************/
   /* now get rid of the appropriate variables */
   /********************************************/
   for (i = 0; i < f->NumVars; i++){
      for (j = 0; j < fdb->NumFiles && !save_it; j++){
         if (j != file_index){
            for (k = 0; k < fdb->NumVars && !save_it; k++){
               if (strcmp(f->VarName[i], fdb->VarName[j])==0){  
                  save_it = 1;
               }
            }
         }
      }
      throw = -1;


      if (!save_it){
         for (j=0; j < fdb->NumVars; j++){
            if (strcmp(f->VarName[i], fdb->VarName[j])==0){
               throw = j;
               j = fdb->NumVars;
            }
         }
         if (throw == -1){
            /* something majorly wront here */
            exit(0);
         }
         if (throw == fdb->NumVars-1){
            fdb->VarName[throw][0] = 0;
            fdb->VarSelected[throw] = 0;
            fdb->VarType[throw] = 0;
            fdb->VarDimensions[throw] = 0; 
            fdb->CharVarLength[throw] = 0;
            fdb->NumVars--;
         }
         else{
            for (j = throw+1; j < fdb->NumVars; j++){
               strcpy(fdb->VarName[j-1], fdb->VarName[j]);
               fdb->VarSelected[j-1] = fdb->VarSelected[j];
            }
            fdb->VarName[j-1][0] = 0;
            fdb->VarSelected[j-1] = 0;
            fdb->NumVars--;
         }
      }
   }
 


   /***************************************/
   /* now free it from the file data base */
   /***************************************/
   free_file_info( fdb->File[file_index] );
   fdb->File[file_index] = NULL;
      
   if (file_index == fdb->NumFiles-1){
      fdb->NumFiles--;
   }
   else{
      for (i = file_index+1; i < fdb->NumFiles; i++){
         fdb->File[i-1] = fdb->File[i];
      }
      fdb->File[i-1] = NULL;
      fdb->NumFiles--;
   }
  
   /*************************************/
   /* Get the correct number of Records */
   /*************************************/
   /*
   nrs = 0;
   for (i = 0; i < fdb->NumFiles; i++){
      if (fdb->File[i]->NumRecs > nrs){
         nrs = fdb->File[i]->NumRecs;
      }
   }
   fdb->NumRecs = nrs;
   */

   /************************************/
   /* Get the correct number of Levels */
   /************************************/
   {
      int lnl = 0;
      for (i = 0; i < fdb->NumFiles; i++){
         if (fdb->File[i]->Levels > lnl){
            lnl = fdb->File[i]->Levels;
         }
      }
      fdb->Levels = lnl;
   }


   /***********************************/   
   /* Get the largest bounding region */
   /***********************************/
   if (fdb->NumFiles == 1){
      fdb->NorthBound = fdb->File[0]->NorthBound;
      fdb->SouthBound = fdb->File[0]->SouthBound;
      fdb->EastBound = fdb->File[0]->EastBound;
      fdb->WestBound = fdb->File[0]->WestBound;
      fdb->TopBound = fdb->File[0]->TopBound;
      fdb->BottomBound = fdb->File[0]->BottomBound;
   }
   else{
      float tb, bb, sb, nb, eb, wb;
      tb = fdb->File[0]->TopBound;
      bb = fdb->File[0]->BottomBound;
      eb = fdb->File[0]->EastBound;
      wb = fdb->File[0]->WestBound;
      nb = fdb->File[0]->NorthBound;
      sb = fdb->File[0]->SouthBound;
      for (i = 1; i < fdb->NumFiles; i++){
         if (fdb->File[i]->TopBound > tb){
            tb = fdb->File[i]->TopBound;
         }
         if (fdb->File[i]->BottomBound < bb){
            bb = fdb->File[i]->BottomBound;
         }
         if (fdb->File[i]->WestBound > wb){
            wb = fdb->File[i]->WestBound;
         }
         if (fdb->File[i]->EastBound < eb){
            eb = fdb->File[i]->EastBound;
         }
         if (fdb->File[i]->NorthBound > nb){
            nb = fdb->File[i]->NorthBound;
         }
         if (fdb->File[i]->SouthBound < sb){
            sb = fdb->File[i]->SouthBound;
         }
      }
      fdb->NorthBound = nb;
      fdb->SouthBound = sb;
      fdb->WestBound = wb;
      fdb->EastBound = eb;
      fdb->TopBound = tb;
      fdb->BottomBound = bb;
   }
   return 1;
}

       

/***************************************/
/* Add a file and it's info to the fdb */
/***************************************/
/* Input: fdb - file database to add   */
/*              file too               */
/*        filename - filename of NetCDF*/
/*             to add to fdb           */
/***************************************/
int add_a_file( FileDB fdb, char *filename)
{
   int  num_times;
   int ds[MAXTIMES], ts[MAXTIMES];
   int fileid, i, j, k;
   struct file_info *F;
   char fln[500];
   int num_recs[MAXTIMES], dont_add_it, num_vars;
   char var_name[MAXVARS][MAX_VAR_LENGTH];
   int var_type[MAXVARS];
   int var_dim[MAXVARS];
   int char_var_length[MAXVARS];
   float topbound, bottombound, eastbound, westbound;
   float northbound, southbound;
   int *recids[MAXTIMES];
   int time[MAXTIMES];
   int general_type, specific_type; 
   NetCDF_Format_Info finfo;
   int levels = 0;
   double varmin[MAXVARS], varmax[MAXVARS];

   strcpy(fln, filename);
   /* make sure not a file already named this */
   for (i = 0; i < fdb->NumFiles; i++){
      if (strcmp(fln, fdb->File[i]->FileName) == 0){
         return 0;
      }
   }

printf("adding file %s\n", filename);

   /************************/
   /* get file type if any */
   /************************/
   if (!Read_NetCDF( filename, &general_type, &specific_type, &fileid)){
	  printf("ERROR: Failed to open file %s as NetCDF\n",filename);
      return 0;
   }
   if (fdb->NumFiles==0){
      fdb->FileType = general_type;
   }
   else if ( general_type != fdb->FileType){
      Close_NetCDF( fileid );                               
      return 0;
   }
   if (specific_type == fsl_netcdf_metar){
      finfo = FSL_METAR;
   }
   else if (specific_type == unidata_netcdf_metar){
      finfo = UNIDATA_METAR;
   }
   else if (specific_type == fsl_netcdf_profile){
      finfo = FSL_PROFILE;
   }
   else{
      printf("Dont't know that type\n");
      return 0;
   }


   /*******************************/   
   /* get times and record number */
   /********************************************/
   if (!Read_NetCDF_Times_and_Recs( finfo, 
                        fileid, &num_times, ts, ds, time, num_recs)){
      Close_NetCDF( fileid );
      return 0;
   }

  
 
   /******************/   
   /* get record IDs */
   /******************/
   for (i = 0; i < num_times; i++){
      recids[i] =  (int *) malloc(sizeof(int)*num_recs[i]);
      if (!recids){
         printf("Error could not allocate memory for record ID's\n");
         exit(0);
      }
      memset(recids[i], -1, sizeof(recids[i]));
   }
   for (i = 0; i < num_times; i++){
      if (!Read_NetCDF_Record_IDs( finfo, fileid,  
                    time[i], recids[i])){
         Close_NetCDF( fileid );
         for (j = 0; j < num_times; j++){
            free(recids[j]);
         }
         return 0;
      }
   }

   
   /************/   
   /* get vars */
   /************/
   if (!Read_NetCDF_Vars( finfo, fileid, &num_vars, var_name,
                                var_type, var_dim, char_var_length,
                                varmin, varmax)){
      Close_NetCDF( fileid );
      for (i = 0; i < num_times; i++){
         free(recids[i]);
      }
      return 0;
   }
   

   /************************/
   /* get levels if needed */
   /************************/
   if (general_type == NETCDF_SOUNDING){
      if (!Read_NetCDF_Num_of_Levels( finfo, fileid, &levels)){
         Close_NetCDF( fileid );
         for (i = 0; i < num_times; i++){
            free(recids[i]);
         }
         return 0;
      }
   }


   /**************/   
   /* get bounds */
   /**************/
   if (!Read_NetCDF_Bounds( finfo, fileid, &westbound, &eastbound,
                                  &northbound, &southbound, &topbound, &bottombound)){
      Close_NetCDF( fileid );                               
      for (i = 0; i < num_times; i++){
         free(recids[i]);
      }
      return 0;
   }


   if (!Close_NetCDF( fileid )){
      for (i = 0; i < num_times; i++){
         free(recids[i]);
      }
      return 0;
   }

 
   F = alloc_file_info();
   strcpy(F->FileName, fln);
   for (i = 0; i < num_times; i++){
      F->RecID[i] = (int *) malloc(sizeof(int)*num_recs[i]);
      if (!F->RecID){
         printf("Error in allocating RecID\n");
         return 0;
      }
   }

   F->FileID = fileid;
   F->FileType = specific_type;
   F->Finfo = finfo;
   /**************************/
   /* add vars to file_info */
   /**************************/
	F->NumVars=0;
	/*
   F->NumVars = num_vars;
	*/

   for( i = 0; i < num_vars; i++){
	  strcpy(F->VarName[F->NumVars], var_name[i]);
	  F->VarType[F->NumVars] = var_type[i];
	  F->CharVarLength[F->NumVars] = char_var_length[i];
	  if(var_type[i] == CHAR_VAR || (varmin[i] < varmax[i])){
		 F->VarSelected[F->NumVars] = 1;
	  }else{
		 F->VarSelected[F->NumVars] = 0;
	  }
	  F->VarDimensions[F->NumVars] = var_dim[i];
	  F->VarMin[F->NumVars] = varmin[i];
	  F->VarMax[F->NumVars] = varmax[i];
	  F->NumVars++;
	  

   }
    
   /**************/
   /* add levels */
   /**************/
   F->Levels = levels;

 
   /**************************/
   /* add times to file_info */
   /**************************/
   F->NumTimes = num_times;
   for (i = 0; i < num_times; i++){
      F->DateStamp[i] = ds[i];
      F->TimeStamp[i] = ts[i];
      F->TimeSeconds[i] = time[i];
      F->TimeSelected[i] = 1;
      F->NumRecs[i] = num_recs[i];
   }

   /*****************************/
   /* add number of record-info */
   /*****************************/
   /*
   F->NumRecs = num_recs;
   */

   /***************/
   /* add rec ids */
   /***************/
   for (i = 0; i < num_times; i++){
      for(j = 0; j < num_recs[i]; j++){
         F->RecID[i][j] = recids[i][j];
      }
      free(recids[i]);
   }


   /***************************/
   /* add bounds to file_info */
   /***************************/
   F->NorthBound = northbound;
   F->SouthBound = southbound;
   F->EastBound = eastbound;
   F->WestBound = westbound;
   F->TopBound = topbound;
   F->BottomBound = bottombound;


   /*******************************************************/
   /* put time into year, month, day, hour, min, secs for */
   /* my own sanity right now, probably won't need it     */
   /*******************************************************/
   /*
   for (i = 0; i < num_times; i++){
      static int dds[24] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,
                         31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
      int iy, im, id, days, day, mon, ij;
      iy = (int)(ds[i] / 1000);
      id = ds[i] - (iy*1000);
      iy += 1900;
      im = (iy % 4) == 0 ? 12 : 0;
      for (ij=im; ij<im+12; ij++) {
        if (id <= dds[ij]) {
          mon = ij-im;
          if (mon > 0) id = id - dds[ij-1];
          break;
        }
      }
      mon++;
      F->Year[i] = iy;
      F->Month[i] = mon;
      F->Day[i] = id;
      F->Hour[i] = (int)(ts[i]/10000);
      F->Minute[i] = (int)((ts[i] - (F->Hour[i] * 10000)) / 100);
      F->Second[i] = ts[i] - ((int)(ts[i] / 100)*100);
   }
   */


   /**************************************/   
   /* attach file_info to file data base */
   /**************************************/
   fdb->File[fdb->NumFiles] = F;
   fdb->NumFiles++;


   /*******************************************************/
   /* add vars to file data base make sure no duplicates */
   /*******************************************************/
   for (j = 0; j < F->NumVars; j++){
      dont_add_it = 0;
      for (i = 0; i < fdb->NumVars && !dont_add_it; i++){
         if (strcmp(F->VarName[j],fdb->VarName[i])==0){
            dont_add_it = 1;
            if (F->VarMin[j] < fdb->VarMin[i]){
               fdb->VarMin[i] = F->VarMin[j];
            }
            if (F->VarMax[j] > fdb->VarMax[i]){
               fdb->VarMax[i] = F->VarMax[j];
            }
         }
      }
      if (!dont_add_it){
         strcpy(fdb->VarName[fdb->NumVars], F->VarName[j]);

         fdb->VarSelected[fdb->NumVars] = F->VarSelected[j]; 
         fdb->VarType[fdb->NumVars] = F->VarType[j];
         fdb->VarDimensions[fdb->NumVars] = F->VarDimensions[j];
         fdb->CharVarLength[fdb->NumVars] = F->CharVarLength[j];
         fdb->VarMin[fdb->NumVars] = F->VarMin[j];
         fdb->VarMax[fdb->NumVars] = F->VarMax[j];
         fdb->NumVars++;
      }
   }
               



   /*******************************************************/
   /* add times to file data base make sure no duplicates */
   /*******************************************************/
   for (j = 0; j < F->NumTimes; j++){
      for (i = 0;   (i < fdb->NumTimes && 
           ((F->DateStamp[j] > fdb->DateStamp[i]) ||
           (F->DateStamp[j] == fdb->DateStamp[i] &&
            F->TimeStamp[j] > fdb->TimeStamp[i]))); i++){
      }
      if (F->DateStamp[j] != fdb->DateStamp[i]  || 
          F->TimeStamp[j] != fdb->TimeStamp[i]){
         for (k = fdb->NumTimes; k > i; k--){
            fdb->DateStamp[k] = fdb->DateStamp[k-1];  
            fdb->TimeStamp[k] = fdb->TimeStamp[k-1];
            fdb->TimeSelected[k] = fdb->TimeSelected[k-1];
            fdb->TimeSeconds[k] = fdb->TimeSeconds[k-1];
         }
         fdb->DateStamp[i] = F->DateStamp[j];
         fdb->TimeStamp[i] = F->TimeStamp[j];
         fdb->TimeSeconds[i] = F->TimeSeconds[j];
         fdb->TimeSelected[i] = F->TimeSelected[j];
         fdb->NumTimes++;
      }
      F->FDB_to_F_timestep[i] = j;
   }

   /*************************************/
   /* add num recs info to data base    */
   /* these have to be redone everytime */
   /* a new file is added so the numrecs*/
   /* dont't become mismatched with the */
   /* appropriate time                  */
   /*************************************/
   for (i = 0; i < fdb->NumTimes; i++){
      fdb->NumRecs[i] = 0;
      for (j = 0; j < fdb->NumFiles; j++){
         for (k = 0; k < fdb->File[j]->NumTimes; k++){
            if (fdb->File[j]->TimeSeconds[k] ==
                                    fdb->TimeSeconds[i]){
               fdb->NumRecs[i] += fdb->File[j]->NumRecs[k];
            }
         }
      }
   }          

   /***********************************************************/
   /* add bounds to file data base picking the largest domain */
   /***********************************************************/
   if (fdb->NumFiles == 1){
      fdb->NorthBound = F->NorthBound;
      fdb->SouthBound = F->SouthBound;
      fdb->EastBound = F->EastBound;
      fdb->WestBound = F->WestBound;
      fdb->TopBound = F->TopBound;
      fdb->BottomBound = F->BottomBound;
   }
   else{
      if (F->NorthBound > fdb->NorthBound){
         fdb->NorthBound = F->NorthBound;
      }
      if (F->SouthBound < fdb->SouthBound){
         fdb->SouthBound = F->SouthBound;
      }
      if (F->WestBound > fdb->WestBound){
         fdb->WestBound = F->WestBound;
      }
      if (F->EastBound < fdb->EastBound){
         fdb->EastBound = F->EastBound;
      }
      if (F->TopBound > fdb->TopBound){
         fdb->TopBound = F->TopBound;
      }
      if (F->BottomBound < fdb->BottomBound){
         fdb->BottomBound = F->BottomBound;
      }
   }

   /**********************/
   /* add levels too fdb */
   /**********************/
   if (F->Levels > fdb->Levels){
      fdb->Levels = F->Levels;
   } 
   return 1;
}

/*********************************************************/
/* This will read the geographics data for one record    */
/*********************************************************/
/* Input: index - index of the fdb, file database to use */
/*        time - time step of desired record             */
/*        record - desired record for location           */
/* Output: lat, lon, hgt - geo postion of record         */
/*********************************************************/
int read_fdb_record_geo_data( int index,  int time,
                              int record, float *lat, float *lon, float *hgt)
{
   FileDB fdb;
   int whichfile, whichfiletime, current_fileid, whichrec;
  
   fdb = get_fdb(index);
   
   whichrec  = fdb->RecIDfilerecid[time][record];
   if (whichrec == -1){
      *lat = MISSING;
      *lon = MISSING;
      *hgt = MISSING;
      return 1;
   }

   whichfile = fdb->RecIDfileindex[time][record];
   whichfiletime = fdb->File[whichfile]->FDB_to_F_timestep[time];

   /***********************************************/
   /* check to see if the file needs to be opened */
   /***********************************************/
   if (fdb->WhichFileOpen != whichfile){
      if (fdb->WhichFileOpen >= 0){
         if (!Close_NetCDF( fdb->File[fdb->WhichFileOpen]->FileID)){
            printf("could not close file\n");
            return 0;
         }
      }
      if (!Open_NetCDF( fdb->File[whichfile]->FileName, &current_fileid)){
         printf("error in read_fdb_records\n");
         return 0;
      }
      fdb->WhichFileOpen = whichfile;
   }
   else{
      current_fileid = fdb->File[fdb->WhichFileOpen]->FileID;
   }

   /*********************/
   /* get location data */
   /*********************/
   if (!Read_NetCDF_Location( fdb->File[whichfile]->Finfo, current_fileid,
        whichrec, lat, lon, hgt)){
      printf("could not read location data\n");
      return 0;
   }
   
   return 1;
}




/****************************************************************/
/* This will read all the data from a record.  It's used by the */
/* irregular_v5d.c calls                                        */
/****************************************************************/
/* Input: index - index into the fdb being used                 */
/*        iv - pointer to the irregular v5d sturct being used   */
/*             the reason why info from the file database is not*/
/*             used is becuase not all times or variable may be */
/*             included from the file database, and the irregular*/
/*             v5d struct contains the desired or chosen vars and*/
/*             times.                                           */
/*        time - the time step for the desired record           */
/*        record - the record to get the data from              */
/*        fdata - array to store the number data in, it will    */
/*                always be of size [0..NumRecs(time)]          */
/*        sdata - array to store the sounding number data in, it*/
/*               will be of size [0..NumLevels * NumSoundingVars]*/
/*               or NULL if not sounding data                   */
/*        cdata - array to store the var string data in, it will*/
/*                be of size [0..VarCharLengh[var]*var], or NULL*/
/*                if no char data                               */
/*        ldata - array to store the level height info it will  */
/*                be size of [0..NumLevels] or NULL if no       */
/*                sounding data                                 */
/****************************************************************/
int read_fdb_record( int index, irregular_v5dstruct *iv, int time,
                      int record, 
                      double *fdata, double *sdata, char *cdata, float *ldata)
{
   int i, k, j, z;
   FileDB fdb;
   double fillvalue;
   int  whichvarid, whichfile, whichrec, whichfiletime;
   static int current_fileid = -1;

   fdb = get_fdb(index);

   whichrec  = fdb->RecIDfilerecid[time][record];
   if (whichrec == -1){
      for (i = 0; i < iv->NumVars; i++){
         fdata[i] = MISSING;
         cdata[iv->CharPointer[i]] = 0;
         if(iv->Type == SOUNDING){
            for (j = iv->SoundingPointer[i];
                   j < iv->SoundingPointer[i]+iv->Levels;
                   j++){
               sdata[j] = MISSING;
            }
         }
      }
      return 1;
   }

   whichfile = fdb->RecIDfileindex[time][record];
   whichfiletime = fdb->File[whichfile]->FDB_to_F_timestep[time];

   /***********************************************/
   /* check to see if the file needs to be opened */
   /***********************************************/
   if (fdb->WhichFileOpen != whichfile){
      if (fdb->WhichFileOpen >= 0){
         if (!Close_NetCDF( fdb->File[fdb->WhichFileOpen]->FileID)){
            printf("could not close file\n");
            return 0;
         }
      }
      if (!Open_NetCDF( fdb->File[whichfile]->FileName, &current_fileid)){
         printf("error in read_fdb_records\n");
         return 0;
      }
      fdb->WhichFileOpen = whichfile;
   }
   else{
      current_fileid = fdb->File[fdb->WhichFileOpen]->FileID;
   }

   /******************************/
   /* get level info if SOUNDING */
   /******************************/
   if (iv->Type == SOUNDING){
      if (!Read_NetCDF_Levels( fdb->File[whichfile]->Finfo, current_fileid,
            whichrec, iv->Levels, ldata)){
         printf("couldn't get level data \n");
         return 0;
      }
   }

   i = -1;
   for( k = 0; k < fdb->NumVars; k++){
      if (fdb->VarSelected[k]){
         whichvarid = fdb->File[whichfile]->VarID[k];
         fillvalue = fdb->File[whichfile]->VarFillValue[k];
         i++;
         /********************************/
         /* set it missing is varid = -1 */
         /********************************/
         if (whichvarid < 0){
            if (fdb->VarType[k] == CHAR_VAR){
               cdata[iv->CharPointer[i]] = 0;
            }
            else if (iv->Type == SOUNDING &&
                     fdb->VarDimensions[k] == VAR_2D){
               for (z = iv->SoundingPointer[i];
                    z < iv->SoundingPointer[i] + iv->Levels;
                    z++){
                  sdata[z] = MISSING;
               }
            }
            else{
               fdata[i] = MISSING;
            }
         } 
         /*********************/            
         /* get sounding data */
         /*********************/
         else if (iv->Type == SOUNDING && fdb->VarType[k] != CHAR_VAR &&
                  fdb->VarDimensions[k] == VAR_2D){
            if (!Read_Sounding_NetCDF_Var_Data(current_fileid, 
                   whichrec, whichvarid, iv->Levels, 
                   (sdata+iv->SoundingPointer[i]))){
               printf("error in read_fdb_records2.1\n");
               return 0;
            }
         }
         /**********************/         
         /* get 1D float data  */
         /**********************/
         else if ((fdb->VarType[k] == FLOAT_VAR || fdb->VarType[k] == DOUBLE_VAR)
              && fdb->VarDimensions[k] == VAR_1D){
            if (!Read_1D_NetCDF_Var_Double_Data( current_fileid,
                 whichrec, whichvarid, &fdata[i])){
               printf("error in read_fdb_records\n");
               return 0;
            }
         }
         /********************/         
         /* get 1D char data */
         /********************/         
         else if (fdb->VarType[k] == CHAR_VAR && fdb->VarDimensions[k] == VAR_1D){
            if (!Read_1D_NetCDF_Var_Char_Data( current_fileid,
                 whichrec, whichvarid, iv->CharVarLength[i],
                 (cdata+iv->CharPointer[i]))){
               printf("error in read_fdb_records2\n");
            }
         }
         /*********************/
         /* get 2D float data */
         /*********************/
         else if (fdb->VarType[k] == FLOAT_VAR && fdb->VarDimensions[k] == VAR_2D){
            int other_dim;
            other_dim = iv->VarName[i][strlen(iv->VarName[i])-1] - '0';
            if (!Read_2D_NetCDF_Var_Double_Data( current_fileid,
                 whichrec, whichvarid, other_dim, &fdata[i])){
               printf("error in read_fdb_records\n");
               return 0;
            }
         }
         /*********************/         
         /* get 2D char data  */
         /*********************/
         else if (fdb->VarType[k] == CHAR_VAR && fdb->VarDimensions[k] == VAR_2D){
            int other_dim;
            other_dim = iv->VarName[i][strlen(iv->VarName[i])-1] - '0';
            if (!Read_2D_NetCDF_Var_Char_Data( current_fileid,
                 whichrec, whichvarid, iv->CharVarLength[i],
                 other_dim, (cdata+iv->CharPointer[i]))){
               printf("error in read_fdb_records2\n");
            }
         }
         /*******************/
         /* get 1D int data */
         /*******************/
         else if (fdb->VarType[k] == INT_VAR && fdb->VarDimensions[k] == VAR_1D){
            int temp_data;
            if (!Read_1D_NetCDF_Var_Int_Data( current_fileid,
                 whichrec, whichvarid, &temp_data)){
               printf("error in read_fdb_records\n");
               return 0;
            }
            fdata[i] = (double)temp_data;       
         }
         /*******************/        
         /* get 2D int data */
         /*******************/        
         else if (fdb->VarType[k] == INT_VAR && fdb->VarDimensions[k] == VAR_2D){
            int temp_data, other_dim;
            other_dim = iv->VarName[i][strlen(iv->VarName[i])-1] - '0';
            if (!Read_2D_NetCDF_Var_Int_Data( current_fileid,
                 whichrec, whichvarid, other_dim, &temp_data)){
               printf("error in read_fdb_records\n");
               return 0;
            }       
            fdata[i] = (double)temp_data;       
         }       
         else{
            printf("don't know that type, sorry\n");
         }

         /*******************************************/
         /* check to see if need to make it MISSING */
         /*******************************************/ 
         if (fdb->VarType[k] != CHAR_VAR){
            if (fdata[i]== fillvalue){
               fdata[i] = MISSING;
            }
         }
      }
   }

   return 1;
}


 
                             
   
/*************************************************************/
/* This will load all the nessecary info about in the fdb or */
/* file database into the irregular v5d struct               */
/*************************************************************/
/* Input: index - index of the fdb being used                */
/*        iv - pointer to the irregular v5d struct           */
/*************************************************************/
int load_fdb_into_v5d( int index, irregular_v5dstruct *iv )
{
   int sp, cp, i, j, k, t, q;
   FileDB fdb;

   fdb = get_fdb(index);

   if (fdb->FileType == NETCDF_SURFACE){
      iv->Type = SURFACE;
   }
   else if (fdb->FileType == NETCDF_SOUNDING){
      iv->Type = SOUNDING;
   }
   else{
      printf("Error in getting iv->Type\n");
      return 0;
   }

   j = 0;
   for (i = 0; i < fdb->NumVars; i++){
      if (fdb->VarSelected[i]){
         j++;
      }
   }
   iv->NumVars = j;

   j = 0;
   for (i = 0; i < fdb->NumTimes; i++){
      if (fdb->TimeSelected[i]){
         j++;
      }
   }
   iv->NumTimes = j;
   iv->Levels = fdb->Levels;

   /**************/
   /* copy times */
   /**************/
   j = 0;
   for (i = 0; i < fdb->NumTimes; i++){
      if (fdb->TimeSelected[i]){
         iv->TimeStamp[j] = fdb->TimeStamp[i];
         iv->DateStamp[j]  = fdb->DateStamp[i];
         iv->TimeSeconds[j] = fdb->TimeSeconds[i];
         iv->NumRecs[j] = fdb->NumRecs[i];
         j++;
      }
   }
   /******************/
   /* copy var names */
   /******************/
   cp = 0;
   sp = 0;
   j = 0;
   for (i = 0; i < fdb->NumVars; i++){
      if (fdb->VarSelected[i]){
         strcpy( iv->VarName[j], fdb->VarName[i]);
         iv->VarMin[j] = fdb->VarMin[i];
         iv->VarMax[j] = fdb->VarMax[i];
         if (fdb->VarType[i] == CHAR_VAR){
            iv->VarType[j] = CHARACTER_VAR;
         }
         else if (fdb->VarDimensions[i] == VAR_2D &&
                  fdb->FileType == NETCDF_SOUNDING){
            iv->VarType[j] = NUMERICAL_VAR_2D;
         }
         else{
            iv->VarType[j] = NUMERICAL_VAR_1D;
         }
         iv->CharVarLength[j] = fdb->CharVarLength[i];

         /**********************/
         /* set up CharPointer */
         /**********************/
         if (iv->VarType[j] == CHARACTER_VAR){
            iv->CharPointer[j] = cp;
            cp += iv->CharVarLength[j];
         }
         else{
            iv->CharPointer[j] = -1;
         }

         /**************************/
         /* set up SoundingPointer */
         /**************************/
         if (iv->VarType[j] == NUMERICAL_VAR_2D){
            iv->SoundingPointer[j] = sp;
            sp += iv->Levels;
         }
         else{
            iv->SoundingPointer[j] = -1;
         }
      
         j++;
      }
   }
   /***************/
   /* copy bounds */
   /***************/
   iv->TopBound = fdb->TopBound;
   iv->BottomBound = fdb->BottomBound;
   iv->WestBound = fdb->WestBound;
   iv->EastBound = fdb->EastBound;
   iv->NorthBound = fdb->NorthBound;
   iv->SouthBound = fdb->SouthBound;


   /************************************************/
   /* create some kind of record list to pass to   */
   /* the v5dstruct for use in calling             */
   /* the RecIDfilerecid is used when calling recs */
   /* for 0 to numrecs it will correspond to the   */
   /* proper file rec id                           */
   /************************************************/
   j = 0;
   for (i = 0; i < fdb->NumTimes; i++){
      if (fdb->TimeSelected[i]){
         fdb->RecIDfileindex[j] = (int *) malloc(sizeof(int)*fdb->NumRecs[i]);
         fdb->RecIDfilerecid[j] = (int *) malloc(sizeof(int)*fdb->NumRecs[i]);
         if (!fdb->RecIDfileindex[j] || !fdb->RecIDfilerecid[j]){
            printf("no memory left\n");
            exit(0);
         }
         j++;
      }
   }

   
   /**************************************************/
   /* this is kind of messy, may be changed later */
   /* but for now, it assigns [0..NumRecs] values */
   /* to the fdb  specifying */
   /* which records in which files to get for a time */
   /**************************************************/ 

   j = 0;
   for (i = 0; i < fdb->NumTimes; i++){
      if (fdb->TimeSelected[i]){
         int rec_count = 0;
         for ( k = 0; k < fdb->NumFiles; k++){
            for (t = 0; t < fdb->File[k]->NumTimes; t++){
               if (fdb->File[k]->TimeSeconds[t] ==
                              fdb->TimeSeconds[i]){
                  for (q = 0; q < fdb->File[k]->NumRecs[t]; q++){
                     fdb->RecIDfileindex[j][rec_count] = k;
                     fdb->RecIDfilerecid[j][rec_count] = 
                          fdb->File[k]->RecID[t][q];
                     rec_count++;
                  }
               }
            }
         }
         /*
         for (p = rec_count; p < fdb->NumRecs; p++){
            fdb->RecIDfileindex[j][p] = -1;
            fdb->RecIDfilerecid[j][p] = -1;
         }
         */
         j++;
      }
   }

   /*************************************************/
   /* This will go through all the File's and get   */
   /* the appropriate var id's for the variables in */
   /* the fdb var list.  This cuts down on time when*/
   /* reading the data                              */
   /*************************************************/
   for (i = 0; i < fdb->NumFiles; i++){
      int curid, vid;
      double fillvalue;
      if (!Open_NetCDF( fdb->File[i]->FileName, &curid)){
         printf("error in getting variable ids\n");
         return 0;
      }
      for (j = 0; j < fdb->NumVars; j++){
         if (fdb->VarDimensions[j] == VAR_1D ||
             (fdb->VarDimensions[j] == VAR_2D &&
              fdb->FileType == NETCDF_SOUNDING)){
            if (!Read_netCDF_Var_ID(curid, fdb->VarName[j], &vid)){
               printf("error2 in getting variable ids\n");
               return 0;
            }
         }
         else if (fdb->VarDimensions[j] == VAR_2D){
            char temp[MAX_VAR_LENGTH];
            strcpy(temp, fdb->VarName[j]);
            temp[strlen(fdb->VarName[j])-1] = 0;
            if (!Read_netCDF_Var_ID(curid, temp, &vid)){
               printf("error3 in getting variable ids\n");
               return 0;
            }
         }
         else{
            printf("error, can't handle var dimension \n");
            return 0;
         }
         fdb->File[i]->VarID[j] = vid;
         /****************************************/
         /* get the fill value too ahead of time */
         /****************************************/
         if (fdb->VarType[j] != CHAR_VAR){
            if (!Read_NetCDF_Fill( curid, vid, &fillvalue)){
               printf("can't get fill value\n");
               return 0;
            }
            fdb->File[i]->VarFillValue[j] = fillvalue;
         }
         else{
            fdb->File[i]->VarFillValue[j] = 0;
         }
      }
   }

   return 1;
}

#endif /* HAVE_LIBNETCDF */


syntax highlighted by Code2HTML, v. 0.9.1