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

/*
 * Append a number of v5d files onto a target file.
 */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <v5d.h>



/*
 * Test if 'str' is in the list of strings given by 'num' and 'list'.
 * Input:  str - the string to test for
 *         num - number of variables in 'list'
 *         list - the list of strings.
 * Return:  -1 = str is not in list
 *          else, returned value is position of str in list starting from 0.
 */
static int member( char *str, int num, char *list[] )
{
   int i;

   for (i=0;i<num;i++) {
      if (strcmp( str, list[i] )==0) {
         /* found */
         return i;
      }
   }
   /* str is not in list */
   return -1;
}




/*
 * Use the header information in 'file' to initialize the given v5dstruct.
 */
static void init_v5dstruct( char *file, v5dstruct *v, int num_omit,
                            char *omitvar[] )
{
   v5dstruct vtemp;
   int i;

   if (v5dOpenFile( file, &vtemp )) {
      memcpy( v, &vtemp, sizeof(v5dstruct) );
      v5dCloseFile( &vtemp );
      v->NumTimes = 0;

      /* setup variables */
      v->NumVars = 0;
      for (i=0;i<vtemp.NumVars;i++) {
         if (member( vtemp.VarName[i], num_omit, omitvar ) == -1 ) {
            strcpy( v->VarName[v->NumVars], vtemp.VarName[i] );
            v->MinVal[v->NumVars] = vtemp.MinVal[i];
            v->MaxVal[v->NumVars] = vtemp.MaxVal[i];
            v->Nl[v->NumVars] = vtemp.Nl[i];
            v->NumVars++;
         }
      }

   }
   else {
      printf("Error: couldn't open %s\n", file );
      exit(1);
   }
}



static void append( int num_infiles, char **infile, char *outfile,
                    v5dstruct *outv )
{
   int i, j, n;
   v5dstruct *inv;
   int error;
   int invar, outvar, intime, outtime;
   float *missing_grid;
   float *grid_buffer;

   /* Initialize missing grid */
   n = outv->Nr * outv->Nc * MAXLEVELS;
   missing_grid = (float *) malloc( n * sizeof(float) );
   grid_buffer = (float *) malloc( n * sizeof(float) );

   if (!missing_grid || !grid_buffer) {
      printf("Error: couldn't allocate temp arrays\n");
      exit(1);
   }

   /* init missing grid data */
   for (i=0;i<n;i++) {
      missing_grid[i] = MISSING;
   }

   for (i=0;i<num_infiles;i++) {
      char *inv_vars[MAXVARS];

      /* Open the next input file */
      inv = v5dOpenFile( infile[i], NULL );
      if (!inv) {
         printf("Error: couldn't open %s\n", infile[i]);
         v5dCloseFile( outv );
         exit(1);
      }

      printf("Appending %s onto %s\n", infile[i], outfile );

      /* Make sure the input file's header info agrees with outv. */
      error = 0;
      if (inv->Nr!=outv->Nr) {
         printf("Error: number of rows in %s (%d) doesn't match %s (%d)\n",
                 infile[i], inv->Nr, outfile, outv->Nr );
         error = 1;
      }
      if (inv->Nc!=outv->Nc) {
         printf("Error: number of column in %s (%d) doesn't match %s (%d)\n",
                 infile[i], inv->Nc, outfile, outv->Nc );
         error = 1;
      }
      if (error) {
         v5dCloseFile(outv);
         printf("Stopping.\n");
         exit(1);
      }


      /* Make a list of pointers to the input file's variables.  We need
       * this to be able to use the member() function later.
       */
      for (j=0;j<inv->NumVars;j++) {
         inv_vars[j] = inv->VarName[j];
      }

      /* append data */
      for (intime=0; intime<inv->NumTimes; intime++) {
         float ga[MAXLEVELS], gb[MAXLEVELS];

         outtime = outv->NumTimes;
         outv->TimeStamp[outtime] = inv->TimeStamp[intime];
         outv->DateStamp[outtime] = inv->DateStamp[intime];
         outv->NumTimes++;

         for (outvar=0; outvar < outv->NumVars; outvar++) {

            /* find corresponding invar */
            invar = member( outv->VarName[outvar], inv->NumVars, inv_vars );

            if (invar==-1) {
               /* the input file is missing the variable we want to write */
               v5dWriteGrid( outv, outtime, outvar, missing_grid );
            }
            else {
               if (inv->CompressMode==outv->CompressMode) {
                  /* faster */
                  v5dReadCompressedGrid( inv, intime, invar, ga, gb, grid_buffer );
                  v5dWriteCompressedGrid( outv, outtime, outvar, ga, gb, grid_buffer );
               }
               else {
                  /* slower */
                  v5dReadGrid( inv, intime, invar, grid_buffer );
                  v5dWriteGrid( outv, outtime, outvar, grid_buffer );
               }
               /* check if outv's min and max have to be updated */
               if (inv->MinVal[invar] < outv->MinVal[outvar]) {
                  outv->MinVal[outvar] = inv->MinVal[invar];
               }
               if (inv->MaxVal[invar] > outv->MaxVal[outvar]) {
                  outv->MaxVal[outvar] = inv->MaxVal[invar];
               }
            }
         } /* for outvar */
      } /* for intime */
   }

   v5dCloseFile(outv);
   printf("Done.\n");
}


#define MAX_INFILES 100


int main( int argc, char *argv[] )
{
   char *infile[MAX_INFILES], *outfile;
   int num_infiles;
   int i;
   v5dstruct v;
   char *omitvar[MAXVARS];
   int num_omit;

   if (argc<3) {
      printf("v5dappend:\n");
      printf("   Append a number of v5d files onto a target file.\n");
      printf("Usage:\n");
      printf("   v5dappend [-var] [...] file.v5d [...] target.v5d\n");
      printf("Arguments:\n");
      printf("   [-var] [...] is an optional list of variables to omit whe creating target.\n");
      printf("   file.v5d [...] is the list of input files.\n");
      printf("   target.v5d is the name of the file to append onto\n");
      return 0;
   }

   /* get arguments */
   num_omit = 0;
   num_infiles = 0;

   for (i=1;i<argc-1;i++) {
      if (argv[i][0]=='-') {
         /* variable to omit */
         omitvar[num_omit] = argv[i]+1;
         num_omit++;
      }
      else {
         if (num_infiles>=MAX_INFILES) {
            printf("Error: too many input files, limit is 100\n");
            break;
         }
         infile[num_infiles] = argv[i];
         num_infiles++;
      }
   }
   outfile = argv[argc-1];
   

   if (v5dUpdateFile( outfile, &v )) {
      /* just append onto outfile */
      printf("Appending onto %s\n", outfile );
      if (num_omit>0) {
         printf("Note: no need to specify variables to omit when appending!\n");
      }
   }
   else {
      /* outfile doesn't exits.  Use first input file to initialize outfile. */
      printf("Creating %s\n", outfile);
      init_v5dstruct( infile[0], &v, num_omit, omitvar );
      if (!v5dCreateFile( outfile, &v )) {
         return 1;
      }
   }

   append( num_infiles, infile, outfile, &v );

   return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1