/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

#include "parser.h"
#include "mrilib.h"

/*-------------------------- global data --------------------------*/

static int                CALC_datum = -1 ;
static int                CALC_nvox  = -1 ;
static PARSER_code *      CALC_code  = NULL ;

static int CALC_nx=-1 , CALC_ny=-1 ;  /* 27 Jul 2000 */

static MRI_IMAGE * CALC_im[26] ;
static int         CALC_type[26] ;
static byte *      CALC_byte[26] ;
static short *     CALC_short[26] ;
static float *     CALC_float[26] ;

static char CALC_output_name[256] = "imcalc.out" ;

/*--------------------------- prototypes ---------------------------*/
void CALC_read_opts( int , char ** ) ;
void CALC_Syntax(void) ;

/*--------------------------------------------------------------------
   read the arguments, load the global variables
----------------------------------------------------------------------*/

void CALC_read_opts( int argc , char * argv[] )
{
   int nopt = 1 ;
   int ids ;

   for( ids=0 ; ids < 26 ; ids++ ){
      CALC_type[ids] = -1 ;
   }

   while( nopt < argc && argv[nopt][0] == '-' ){

      /**** -nxy nx ny ****/

      if( strncmp(argv[nopt],"-nxy",4) == 0 ){
         if( ++nopt >= argc-1 ){
            fprintf(stderr,"need 2 arguments after -nxy!\n"); exit(1);
         }
         CALC_nx = strtol(argv[nopt++],NULL,10) ;
         CALC_ny = strtol(argv[nopt++],NULL,10) ;
         continue ;  /* go to next arg */
      }

      /**** -datum type ****/

      if( strncmp(argv[nopt],"-datum",6) == 0 ){
         if( ++nopt >= argc ){
            fprintf(stderr,"need an argument after -datum!\n") ; exit(1) ;
         }
         if( strcmp(argv[nopt],"short") == 0 ){
            CALC_datum = MRI_short ;
         } else if( strcmp(argv[nopt],"float") == 0 ){
            CALC_datum = MRI_float ;
         } else if( strcmp(argv[nopt],"byte") == 0 ){
            CALC_datum = MRI_byte ;
         } else {
            fprintf(stderr,"-datum of type '%s' is not supported in 3dmerge!\n",
                    argv[nopt] ) ;
            exit(1) ;
         }
         nopt++ ; continue ;  /* go to next arg */
      }

      /**** -output name ****/

      if( strncmp(argv[nopt],"-output",6) == 0 ){
         nopt++ ;
         if( nopt >= argc ){
            fprintf(stderr,"need argument after -output!\n") ; exit(1) ;
         }
         strncpy( CALC_output_name , argv[nopt++] , 255 ) ; CALC_output_name[255] = '\0' ;
         continue ;
      }

      /**** -expr expression ****/

      if( strncmp(argv[nopt],"-expr",4) == 0 ){
         if( CALC_code != NULL ){
            fprintf(stderr,"cannot have 2 -expr options!\n") ; exit(1) ;
         }
         nopt++ ;
         if( nopt >= argc ){
            fprintf(stderr,"need argument after -expr!\n") ; exit(1) ;
         }
         CALC_code = PARSER_generate_code( argv[nopt++] ) ;
         if( CALC_code == NULL ){
            fprintf(stderr,"illegal expression!\n") ; exit(1) ;
         }
         continue ;
      }

      /**** -[a-z] filename ****/

      ids = strlen(argv[nopt]) ;

      if( (argv[nopt][1] >= 'a' && argv[nopt][1] <= 'z') && (ids == 2) ){

         int ival , nxyz ;
         MRI_IMAGE * im ;

         ival = argv[nopt][1] - 'a' ;
         if( CALC_im[ival] != NULL ){
            fprintf(stderr,"can't open duplicate %s images!\n",argv[nopt]) ;
            exit(1) ;
         }

         nopt++ ;
         if( nopt >= argc ){
            fprintf(stderr,"need argument after %s!\n",argv[nopt-1]) ; exit(1) ;
         }

         im = mri_read_just_one( argv[nopt++] ) ;
         if( im == NULL ){
            fprintf(stderr,"can't open image %s\n",argv[nopt-1]) ; exit(1) ;
         }

         nxyz = im->nvox ;
         if( CALC_nvox < 0 ){
            CALC_nvox = nxyz ;
         } else if( nxyz != CALC_nvox ){
            fprintf(stderr,"image %s differs in size from others\n",argv[nopt-1]);
            exit(1) ;
         }

         CALC_type[ival] = im->kind ;
         CALC_im[ival]   = im ;

         switch( CALC_type[ival] ){
            default: fprintf(stderr,"illegal datum in image %s\n",argv[nopt-1]);
                     exit(1) ;

            case MRI_short: CALC_short[ival] = MRI_SHORT_PTR(im) ; break ;
            case MRI_float: CALC_float[ival] = MRI_FLOAT_PTR(im) ; break ;
            case MRI_byte : CALC_byte [ival] = MRI_BYTE_PTR(im)  ; break ;
         }

         if( CALC_datum < 0 ) CALC_datum = CALC_type[ival] ;
         continue ;
      }

      fprintf(stderr,"Unknown option: %s\n",argv[nopt]) ; exit(1) ;

   }  /* end of loop over options */

   /*** cleanup ***/

   if( nopt < argc ){
     fprintf(stderr,"Extra command line arguments puzzle me! %s ...\n",argv[nopt]) ;
     exit(1) ;
   }

   for( ids=0 ; ids < 26 ; ids++ ) if( CALC_im[ids] != NULL ) break ;

   if( ids == 26 && (CALC_nx < 2 || CALC_ny < 2) ){
      fprintf(stderr,"No input images given!\n") ; exit(1) ;
   }

   if( CALC_code == NULL ){
      fprintf(stderr,"No expression given!\n") ; exit(1) ;
   }

   return ;
}

/*------------------------------------------------------------------*/

void CALC_Syntax(void)
{
   printf(
    "Do arithmetic on 2D images, pixel-by-pixel.\n"
    "Usage: imcalc options\n"
    "where the options are:\n"
   ) ;

   printf(
    "  -datum type = Coerce the output data to be stored as the given type,\n"
    "                  which may be byte, short, or float.\n"
    "                  [default = datum of first input image on command line]\n"
    "  -a dname    = Read image 'dname' and call the voxel values 'a'\n"
    "                  in the expression.  'a' may be any letter from 'a' to 'z'.\n"
    "               ** If some letter name is used in the expression, but not\n"
    "                  present in one of the image options here, then that\n"
    "                  variable is set to 0.\n"
    "  -expr \"expression\"\n"
    "                Apply the expression within quotes to the input images,\n"
    "                  one voxel at a time, to produce the output image.\n"
    "                  (\"sqrt(a*b)\" to compute the geometric mean, for example)\n"
    "  -output name = Use 'name' for the output image filename.\n"
    "                  [default='imcalc.out']\n"
    "\n"
    "See the output of '3dcalc -help' for details on what kinds of expressions\n"
    "are possible.  Note that complex-valued images cannot be processed (byte,\n"
    "short, and float are OK).\n"
   ) ;
   exit(0) ;
}

/*------------------------------------------------------------------*/

int main( int argc , char * argv[] )
{
   double atoz[26] ;
   int ii , ids ;
   MRI_IMAGE * new_im=NULL ;
   float * fnew ;

   /*** read input options ***/

   if( argc < 2 || strncmp(argv[1],"-help",4) == 0 ) CALC_Syntax() ;

   machdep() ;

   CALC_read_opts( argc , argv ) ;

   /*** make output image (always float datum) ***/

   if( THD_filesize(CALC_output_name) > 0 ){
      fprintf(stderr,
              "*** Output file %s already exists -- cannot continue!\n",
              CALC_output_name ) ;
      exit(1) ;
   }

   for( ids=0 ; ids < 26 ; ids++ ) if( CALC_im[ids] != NULL ) break ;
   if( ids < 26 ){
      new_im = mri_new_conforming( CALC_im[ids] , MRI_float ) ;
   } else if( CALC_nx > 1 && CALC_ny > 1 ){
      new_im = mri_new( CALC_nx , CALC_ny , MRI_float ) ;
      CALC_nvox = new_im->nvox ;
   }
   fnew = MRI_FLOAT_PTR(new_im) ;

   for( ids=0 ; ids < 26 ; ids++ ) atoz[ids] = 0.0 ;

   /*** loop over voxels and compute result ***/

   for( ii=0 ; ii < CALC_nvox ; ii++ ){

      for( ids=0 ; ids < 26 ; ids++ ){
         switch( CALC_type[ids] ){
            case MRI_short: atoz[ids] = CALC_short[ids][ii] ; break;
            case MRI_float: atoz[ids] = CALC_float[ids][ii] ; break;
            case MRI_byte : atoz[ids] = CALC_byte [ids][ii] ; break;
         }
      }

      fnew[ii] = PARSER_evaluate_one( CALC_code , atoz ) ;
   }

   /*** toss input images ***/

   for( ids=0 ; ids < 26 ; ids++ )
      if( CALC_im[ids] != NULL ) mri_free( CALC_im[ids] ) ;

   /*** scale to output image, if needed ***/

   switch( CALC_datum ){

      case MRI_short:{
         float top ;
         MRI_IMAGE * shim ;

         top = mri_maxabs( new_im ) ;
         if( top < 32767.0 ) shim = mri_to_short( 1.0 , new_im ) ;
         else                shim = mri_to_short_scl( 0.0 , 10000.0 , new_im ) ;

         mri_free(new_im) ; new_im = shim ;
      }
      break ;

      case MRI_byte:{
         float top ;
         MRI_IMAGE * bim ;

         top = mri_maxabs( new_im ) ;
         if( top < 255.0 ) bim = mri_to_byte_scl( 1.0 ,   0.0 , new_im ) ;
         else              bim = mri_to_byte_scl( 0.0 , 255.0 , new_im ) ;

         mri_free(new_im) ; new_im = bim ;
      }
      break ;
   }

   /*** done ***/

   mri_write( CALC_output_name , new_im ) ;
   exit(0) ;
}
