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

/* This is the wrapper for Vis5D external analysis functions. */



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>     /* must be before sys/resource.h */
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "socketio.h"
#include "v5d.h"      /* To get MAXTIMES, etc */



static int Nr, Nc, Nl[MAXLEVELS], LowLev[MAXLEVELS], MaxNl;
static int NumTimes, NumVars;

static int Projection, Vertical;
static float Proj_Args[100], Vert_Args[MAXLEVELS];

static float ProbeRow, ProbeCol, ProbeLev;
static float ProbeLat, ProbeLon, ProbeHgt;
static float ProbeValue[MAXVARS];

#define NUMARGS 5
static float Argument[NUMARGS]; /* only use is commented out;
                                   argument function not called */


/* Define this if using McIDAS library */
#define LIB_MCIDAS



/*
 * Create a socket with the given Unix-domain name.
 * Input:  socket name
 * Return: socket number or -1 if error.
 */
static int create_socket( char *name )
{
   struct sockaddr_un addr;
   int len, sock;
   int tries;

   /* create socket */
   sock = socket( PF_UNIX, SOCK_STREAM, 0 );
   if (sock<0) {
      perror("function creating socket");
      return -1;
   }

   /* try to connect socket to given name */
   /*printf("EXTMAIN: creating socket named: %s\n", name );*/
   strcpy( addr.sun_path, name );
   addr.sun_family = AF_UNIX;
   len = strlen(addr.sun_path)+sizeof(addr.sun_family);
   for (tries=0;tries<5;tries++) {
      if (connect(sock, (struct sockaddr *) &addr, len) < 0) {
         perror("External Function Error: function connect failed");
         /*printf("   failed on try %d\n", tries );*/
      }
      else {
         /* success !*/
         return sock;
      }
   }

   /* error */
   return -1;
}




/*** call_user_function ***********************************************
   Get arguments from Vis5D, compute new grid data by calling user
   function and return results back to Vis5D.
   Input: sock - socket number
   Return:  1 = OK, 0 = error.
**********************************************************************/
static int call_user_function( int sock )
{
   int it, iv, i;
   float *ingrid, *outgrid;
   char names[MAXVARS][8];
   int error_flag;
   int outnl, outlowlev;

   /*** First we get the parameter which won't change for each timestep ***/

   /* get number of time steps and variables */
   receive_int( sock, &NumTimes );
   receive_int( sock, &NumVars );

   /*printf("EXTMAIN: times:%d vars:%d\n", NumTimes, NumVars);*/

   /* Receive size of grids */
   receive_int( sock, &Nr );
   receive_int( sock, &Nc );
   MaxNl = 0;
   for (iv=0;iv<NumVars;iv++) {
      receive_int( sock, &Nl[iv] );
      if (Nl[iv]>MaxNl) {
         MaxNl = Nl[iv];
      }
   }
   for (iv=0;iv<NumVars;iv++) {
      receive_int( sock, &LowLev[iv] );
   }

   /* Receive variable names */
   for (iv=0;iv<NumVars;iv++) {
      int i, j;
      char name[100];
      receive_str( sock, name );
      /* copy name into names[iv] */
      for (i=0;i<8 && name[i];i++)
        names[iv][i] = name[i];
      for (j=i;j<8;j++)
        names[iv][j] = ' ';
   }

   /* Receive map proj and vertical coord sys info */
   receive_int( sock, &Projection );
   for (i=0;i<100;i++) {
      receive_float( sock, &Proj_Args[i] );
   }
   receive_int( sock, &Vertical );
   for (i=0;i<MAXLEVELS;i++) {
      receive_float( sock, &Vert_Args[i] );
   }

   /* Receive probe location */
   receive_float( sock, &ProbeRow );
   receive_float( sock, &ProbeCol );
   receive_float( sock, &ProbeLev );
   receive_float( sock, &ProbeLat );
   receive_float( sock, &ProbeLon );
   receive_float( sock, &ProbeHgt );

   /* Receive user arguments */
/*
   for (i=0;i<NUMARGS;i++) {
      receive_float( sock, &Argument[i] );
   }
*/

   /* allocate dynamic arrays */
   /*printf("EXTMAIN: Nr=%d Nc=%d Nl=%d NumVars=%d\n", Nr, Nc, Nl, NumVars );*/
   ingrid = (float *) malloc( Nr*Nc*MaxNl*NumVars*sizeof(float) );
   outgrid = (float *) malloc( Nr*Nc*MaxNl*sizeof(float) );
   if (!ingrid || !outgrid) {
      printf("External Function Error: out of memory\n");
      send_int( sock, -1 );
      return 0;
   }

   /* the user function will probably be called more than once.... */
   while (1) {
      int mcfile, mcgrid;
      int date, time;
      int error;

      /* get which timestep to compute */
      receive_int( sock, &it );
      /*printf("EXTMAIN: it=%d\n",it);*/
      if (it<0) {
         /* all done */
         break;
      }

      printf("Computing time step %d\n", it );

      /*** Now receive parameters which change for each timestep */

      /* receive date and time info */
      receive_int( sock, &date );
      /*printf("EXTMAIN: date = %d\n", date );*/
      receive_int( sock, &time );
      /*printf("EXTMAIN: time = %d\n", time );*/

      /* Receive Probe values */
      for (iv=0;iv<NumVars;iv++) {
         receive_float( sock, &ProbeValue[iv] );
      }

      /*printf("EXTMAIN: foo\n");*/

      /*** get the ingrid data ***/
      error_flag = 0;
      for (iv=0;iv<NumVars;iv++) {

         receive_int( sock, &mcfile );
         receive_int( sock, &mcgrid );
/*         printf("EXTMAIN: file = %d\n", mcfile );*/
/*         printf("EXTMAIN: grid = %d\n", mcgrid );*/

         if (mcfile==0 && mcgrid==0) {
            /* Vis5D is sending us the grid data. */
            receive_data( sock, ingrid+iv*Nr*Nc*MaxNl,
                          Nr*Nc*Nl[iv]*sizeof(float) );
         }
         else {
            /* read grid data from the original McIDAS grid file. */
            if (!get_mcgrid( mcfile, mcgrid, ingrid+iv*Nr*Nc*Nl[iv], iv )) {
               /* error */
               printf("External Function Error: Couldn't read GR3D%04d, grid %d\n",
                      mcfile, mcgrid );
               error_flag = 1;
            }
         }
      }

      /*** call user function ***/
      if (!error_flag) {
         /*printf("EXTMAIN: Calling user function\n");*/
         error = F77_FUNC(userfunc,USERFUNC)( outgrid, &outnl, &outlowlev,
                        ingrid, &Nr, &Nc, Nl, LowLev, &MaxNl, &NumVars, names,
                           &date, &time,
                           &Projection, Proj_Args, &Vertical, Vert_Args );
                         /* &south, &west, &deltalat, &deltalon, height ); */
         if (error!=0) {
            printf("Error in user function, return code was: %d\n", error );
         }
      }
      else {
         error = 1;
      }

/*      printf("EXTMAIN: error=%d\n", error );*/

      /* Return the error/ok code */
      send_int( sock, error );

      /* Return the results of the computation to Vis5D */
      if (error==0) {
         /* send back the new grid */
/*         printf("OutNl = %d\n", outnl );*/
         if (outnl>MaxNl)  outnl = MaxNl;
         send_int( sock, outnl );
         send_int( sock, outlowlev );
         /* we used to return compressed data...oh well */
         send_data( sock, outgrid, Nr*Nc*outnl*sizeof(float) );
      }

   }

   free(ingrid);
   free(outgrid);
   return 1;
}




/*
 *
 * main()
 *
 */
#ifdef LIB_MCIDAS
   extern int Argc;
   extern char **Argvector;
F77_FUNC(main0,MAIN0)()
   {
      int argc = Argc;
      char **argv = Argvector;
#else
   /* Not using McIDAS lib */
   main( int argc, char *argv[] )
   {
#endif
   int sock;

   if (argc!=2) {
      printf("Parameter Error: no socket name.\n");
      printf("This is a Vis5D external function.  It can only be used\n");
      printf("via vis5d.\n");
      exit(1);
   }

   sleep(1);

   sock = create_socket( argv[1] );
   if (sock>=0) {
      /* send an acknowledgment signal */
      send_int( sock, 0x1234 );
      /* call user function */
      call_user_function( sock );
      /* finish up */
      close(sock);
   }

   return 0;
}



/*** get_mcgrid *******************************************************
   Get a McIDAS grid.
   Input: file - number of the McIDAS grid file.
          grid - which grid in the file.
          data - pointer to buffer to put data.
          var - which variable this grid is.
   Output: data - read from the file.
   Return: 1 = ok, 0 = error.
**********************************************************************/
int get_mcgrid( int file, int grid, float *data, int var )
{
   int result, max, nr, nc, nl, table[64];

   printf("Getting McIDAS grid %d %d 0x%x\n", file, grid, data );
   if (file==0 && grid==0) {
      memset( data, 0, Nr*Nc*MaxNl*sizeof(float) );
      printf("Filling grid with zeros\n");
   }
   else {
      max = Nr*Nc*MaxNl;
      memset( data, 0, Nr*Nc*MaxNl*sizeof(float) );
      result = F77_FUNC(iggt3d,IGGT3D) ( &file, &grid, &max, data, &nr, &nc, &nl, table );

      /*printf("result=%d nr=%d nc=%d nl=%d\n", result, nr, nc, nl );*/
      if (result!=0 || nr!=Nr || nc!=Nc || nl!=Nl[var]) {
         printf("External Function Error: get_mcgrid error: result=%d\n", result);
         return 0;
      }
   }
   return 1;
}



void F77_FUNC(pf,PF)( float *x )
{
   printf("%g\n", *x);
}



/*
 * Return the current probe position in (row,column,level) and
 * (latitude, longitude, altitude km).
 */
void F77_FUNC(probepos,PROBEPOS)( float *row, float *col, float *lev,
               float *lat, float *lon, float *alt )
{
   *row = ProbeRow + 1.0;
   *col = ProbeCol + 1.0;
   *lev = ProbeLev + 1.0;
   *lat = ProbeLat;
   *lon = ProbeLon;
   *alt = ProbeHgt;
}



/*
 * Return the value of a variable at the probe's location.
 * Input:  var - which variable  [1..NumVars]
 */
float F77_FUNC(probeval,PROBEVAL)( int *var )
{
   if (*var>0 && *var<=NumVars) {
      return ProbeValue[ *var-1 ];
   }
   else {
      return 1.0e30;   /* MISSING VALUE */
   }
}



/*
 * Return the value of a user-specified argument.  The argument(s) are
 * entered by the user into a GUI form when invoking the function.
 * Input:  n - an argument number in [1..]
 * Return:  the value of the argument or 0 if n is bad.
 */
float F77_FUNC(argument,ARGUMENT)( int *n )
{
   if (*n<=0 || *n>NUMARGS) {
      return 0.0;
   }
   else {
      return Argument[*n-1];
   }
}



syntax highlighted by Code2HTML, v. 0.9.1