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

#ifndef ALLOW_PLUGINS
#  error "Plugins not properly set up -- see machdep.h"
#endif

/***********************************************************************
  Plugin to compute statistics over ROI.
************************************************************************/

char * MASKAVE_main( PLUGIN_interface * ) ;

static char helpstring[] =
   " Purpose: Compute statistics over a mask-selected ROI.\n"
   "\n"
   " Source:  Dataset   = data to be averaged\n"
   "          Sub-brick = which one to use\n"
   "                      [if -1 is input, will do all sub-bricks]\n\n"
   " Mask:    Dataset   = masking dataset\n"
   "          Sub-brick = which one to use\n\n"
   " Range:   Bottom    = min value from mask dataset to use\n"
   "          Top       = max value from mask dataset to use\n"
   "          [if Bottom >  Top, then all nonzero mask voxels will be used; ]\n"
   "          [if Bottom <= Top, then only nonzero mask voxels in this range]\n"
   "          [                  will be used in computing the statistics.  ]\n\n"
   " 1D Save: Name      = If all input sub-bricks are used (i.e., setting\n"
   "                      'Source Sub-brick' = -1 above), then this option\n"
   "                      lets you save the resulting average time series\n"
   "                      into the internal list of timeseries available via\n"
   "                      the 'Choose Timeseries', 'Pick Ideal', ... buttons.\n"
   "          To Disk?  = If 'YES' is chosen, then will also write the\n"
   "                      timeseries to disk in the *.1D format.\n\n"
   " Author -- RW Cox"
;

#define NUM_yesno_list 2
static char *yesno_list[] = { "YES" , "NO" } ;


/***********************************************************************
   Set up the interface to the user
************************************************************************/


DEFINE_PLUGIN_PROTOTYPE

PLUGIN_interface * PLUGIN_init( int ncall )
{
   PLUGIN_interface * plint ;

   if( ncall > 0 ) return NULL ;  /* only one interface */

   CHECK_IF_ALLOWED("ROIAVERAGE","ROI Average") ;  /* 30 Sep 2016 */

   /*-- set titles and call point --*/

   plint = PLUTO_new_interface( "ROI Average" , "Average Dataset over ROI" ,
                                helpstring , PLUGIN_CALL_VIA_MENU ,
                                (cptr_func *)MASKAVE_main  ) ;

   PLUTO_add_hint( plint , "Average Dataset over ROI" ) ;

   PLUTO_set_sequence( plint , "A:afniinfo:dset" ) ;

   /*-- first line of input --*/

   PLUTO_add_option( plint , "Source" , "Source" , TRUE ) ;
   PLUTO_add_dataset(plint , "Dataset" ,
                                    ANAT_ALL_MASK , FUNC_ALL_MASK ,
                                    DIMEN_ALL_MASK | BRICK_ALLREAL_MASK ) ;

   PLUTO_add_number( plint , "Sub-brick" , -1,9999,0 , 0,1 ) ;

   /*-- second line of input --*/

   PLUTO_add_option( plint , "Mask" , "Mask" , TRUE ) ;
   PLUTO_add_dataset( plint , "Dataset" ,
                                 ANAT_ALL_MASK , FUNC_ALL_MASK ,
                                 DIMEN_ALL_MASK | BRICK_ALLREAL_MASK ) ;
   PLUTO_add_number( plint , "Sub-brick" , 0,9999,0 , 0,1 ) ;  /* 06 Aug 1998 */

   /*-- third line of input --*/

   PLUTO_add_option( plint , "Range"  , "Range" , FALSE ) ;
   PLUTO_add_number( plint , "Bottom" , -99999,99999, 1, 0,1 ) ;
   PLUTO_add_number( plint , "Top"    , -99999,99999,-1, 0,1 ) ;

   /*-- 4th line of input (06 Aug 1998) --*/

   PLUTO_add_option( plint , "1D Save" , "1D Save" , FALSE ) ;
   PLUTO_add_string( plint , "Name" , 0,NULL , 12 ) ;
   PLUTO_add_string( plint , "To Disk?" , NUM_yesno_list , yesno_list , 1 ) ;

   return plint ;
}

/***************************************************************************
  Main routine for this plugin (will be called from AFNI).
****************************************************************************/

char * MASKAVE_main( PLUGIN_interface * plint )
{
   MCW_idcode * idc ;
   THD_3dim_dataset * input_dset , * mask_dset ;
   int iv , mcount , nvox , ii , sigmait , nvals=0 , doall , ivbot,ivtop ;
   float mask_bot=666.0 , mask_top=-666.0 ;
   double sum=0.0 , sigma=0.0 ;
   float * sumar=NULL , * sigmar=NULL ;
   char * tag , * str , buf[64] , abuf[32],sbuf[32] ;
   byte * mmm ;

   char * cname=NULL ;  /* 06 Aug 1998 */
   int    cdisk=0 ;     /* 22 Aug 2000 */
   int miv=0 ;

   /*--------------------------------------------------------------------*/
   /*----- Check inputs from AFNI to see if they are reasonable-ish -----*/

   if( plint == NULL )
      return "*************************\n"
             "MASKAVE_main:  NULL input\n"
             "*************************"  ;

   /*-- read 1st line --*/

   PLUTO_next_option(plint) ;
   idc        = PLUTO_get_idcode(plint) ;
   input_dset = PLUTO_find_dset(idc) ;
   if( input_dset == NULL )
      return "********************************\n"
             "MASKAVE_main:  bad input dataset\n"
             "********************************"  ;

   iv = (int) PLUTO_get_number(plint) ;
   if( iv >= DSET_NVALS(input_dset) )
      return "**********************************\n"
             "MASKAVE_main:  bad input sub-brick\n"
             "**********************************" ;
   doall = (iv < 0) ;
   if( doall ){
      nvals  = DSET_NVALS(input_dset) ;
      ivbot  = 0 ; ivtop = nvals-1 ;
   } else {
      ivbot = ivtop = iv ;
   }
   DSET_load(input_dset) ;
   if( DSET_ARRAY(input_dset,ivbot) == NULL )
      return "*********************************\n"
             "MASKAVE_main:  can't load dataset\n"
             "*********************************"  ;
   nvox = DSET_NVOX(input_dset) ;

   /*-- read 2nd line --*/

   PLUTO_next_option(plint) ;
   idc       = PLUTO_get_idcode(plint) ;
   mask_dset = PLUTO_find_dset(idc) ;

   if( mask_dset == NULL )
      return "*******************************\n"
             "MASKAVE_main:  bad mask dataset\n"
             "*******************************"  ;

   if( DSET_NVOX(mask_dset) != nvox )
      return "*************************************************************\n"
             "MASKAVE_main: mask input dataset doesn't match source dataset\n"
             "*************************************************************"  ;

   miv = (int) PLUTO_get_number(plint) ;  /* 06 Aug 1998 */
   if( miv >= DSET_NVALS(mask_dset) )
      return "*****************************************************\n"
             "MASKAVE_main: mask dataset sub-brick index is too big\n"
             "*****************************************************"  ;

   DSET_load(mask_dset) ;
   if( DSET_ARRAY(mask_dset,0) == NULL )
      return "**************************************\n"
             "MASKAVE_main:  can't load mask dataset\n"
             "**************************************"  ;

   /*-- read optional lines --*/

   while( (tag=PLUTO_get_optiontag(plint)) != NULL ){

      if( strcmp(tag,"Range") == 0 ){
         mask_bot = PLUTO_get_number(plint) ;
         mask_top = PLUTO_get_number(plint) ;
         continue ;
      }

      if( strcmp(tag,"1D Save") == 0 ){
         char * yn ;
         cname = PLUTO_get_string(plint) ;
         yn    = PLUTO_get_string(plint) ;
         cdisk = (strcmp(yn,yesno_list[0]) == 0) ;
         continue ;
      }

   }

   /*------------------------------------------------------*/
   /*---------- At this point, the inputs are OK ----------*/

   /*-- build a byte mask array --*/

   mmm = (byte *) malloc( sizeof(byte) * nvox ) ;
   if( mmm == NULL )
      return "*** Can't malloc workspace! ***" ;

   /* separate code for each input data type */

   switch( DSET_BRICK_TYPE(mask_dset,miv) ){
      default:
         free(mmm) ;
         return "*** Can't use mask dataset -- illegal data type! ***" ;

      case MRI_short:{
         short mbot , mtop ;
         short * mar = (short *) DSET_ARRAY(mask_dset,miv) ;
         float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ;
         if( mfac == 0.0 ) mfac = 1.0 ;
         if( mask_bot <= mask_top ){
            mbot = SHORTIZE(mask_bot/mfac) ;
            mtop = SHORTIZE(mask_top/mfac) ;
         } else {
            mbot = (short) -MRI_TYPE_maxval[MRI_short] ;
            mtop = (short)  MRI_TYPE_maxval[MRI_short] ;
         }
         for( mcount=0,ii=0 ; ii < nvox ; ii++ )
            if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ){ mmm[ii] = 1 ; mcount++ ; }
            else                                                    { mmm[ii] = 0 ; }
      }
      break ;

      case MRI_byte:{
         byte mbot , mtop ;
         byte * mar = (byte *) DSET_ARRAY(mask_dset,miv) ;
         float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ;
         if( mfac == 0.0 ) mfac = 1.0 ;
         if( mask_bot <= mask_top ){
            mbot = BYTEIZE(mask_bot/mfac) ;
            mtop = BYTEIZE(mask_top/mfac) ;
            if( mtop == 0 ){
               free(mmm) ;
               return "*** Illegal mask range for mask dataset of bytes. ***" ;
            }
         } else {
            mbot = 0 ;
            // [PT: Dec 22, 2020] Thanks for the fix here, C Rorden!
            mtop = (byte) MRI_TYPE_maxval[MRI_byte] ; 
         }
         for( mcount=0,ii=0 ; ii < nvox ; ii++ )
            if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ){ mmm[ii] = 1 ; mcount++ ; }
            else                                                    { mmm[ii] = 0 ; }
      }
      break ;

      case MRI_float:{
         float mbot , mtop ;
         float * mar = (float *) DSET_ARRAY(mask_dset,miv) ;
         float mfac = DSET_BRICK_FACTOR(mask_dset,miv) ;
         if( mfac == 0.0 ) mfac = 1.0 ;
         if( mask_bot <= mask_top ){
            mbot = (float) (mask_bot/mfac) ;
            mtop = (float) (mask_top/mfac) ;
         } else {
            mbot = -WAY_BIG ;
            mtop =  WAY_BIG ;
         }
         for( mcount=0,ii=0 ; ii < nvox ; ii++ )
            if( mar[ii] >= mbot && mar[ii] <= mtop && mar[ii] != 0 ){ mmm[ii] = 1 ; mcount++ ; }
            else                                                    { mmm[ii] = 0 ; }
      }
      break ;
   }

   if( mcount == 0 ){
      free(mmm) ;
      return "*** No voxels survive the masking operations! ***" ;
   }
   sigmait = (mcount > 1) ;

   /*-- compute statistics --*/

   if( doall ){
      sumar  = (float *) malloc( sizeof(float) * nvals ) ;
      sigmar = (float *) malloc( sizeof(float) * nvals ) ;
   }

   for( iv=ivbot ; iv <= ivtop ; iv++ ){
      sum = sigma = 0.0 ;                         /* 13 Dec 1999 */
      switch( DSET_BRICK_TYPE(input_dset,iv) ){

         default:
            free(mmm) ; if( doall ){ free(sumar) ; free(sigmar) ; }
            return "*** Can't use source dataset -- illegal data type! ***" ;

         case MRI_short:{
            short * bar = (short *) DSET_ARRAY(input_dset,iv) ;
            float mfac = DSET_BRICK_FACTOR(input_dset,iv) ;
            if( mfac == 0.0 ) mfac = 1.0 ;

            for( ii=0 ; ii < nvox ; ii++ ) if( mmm[ii] ) sum += bar[ii] ;
            sum = sum / mcount ;

            if( sigmait ){
               for( ii=0 ; ii < nvox ; ii++ )
                  if( mmm[ii] ) sigma += SQR(bar[ii]-sum) ;
               sigma = mfac * sqrt( sigma/(mcount-1) ) ;
            }
            sum = mfac * sum ;
         }
         break ;

         case MRI_byte:{
            byte * bar = (byte *) DSET_ARRAY(input_dset,iv) ;
            float mfac = DSET_BRICK_FACTOR(input_dset,iv) ;
            if( mfac == 0.0 ) mfac = 1.0 ;

            for( ii=0 ; ii < nvox ; ii++ ) if( mmm[ii] ) sum += bar[ii] ;
            sum = sum / mcount ;

            if( sigmait ){
               for( ii=0 ; ii < nvox ; ii++ )
                  if( mmm[ii] ) sigma += SQR(bar[ii]-sum) ;
               sigma = mfac * sqrt( sigma/(mcount-1) ) ;
            }
            sum = mfac * sum ;
         }
         break ;

         case MRI_float:{
            float * bar = (float *) DSET_ARRAY(input_dset,iv) ;
            float mfac = DSET_BRICK_FACTOR(input_dset,iv) ;
            if( mfac == 0.0 ) mfac = 1.0 ;

            for( ii=0 ; ii < nvox ; ii++ ) if( mmm[ii] ) sum += bar[ii] ;
            sum = sum / mcount ;

            if( sigmait ){
               for( ii=0 ; ii < nvox ; ii++ )
                  if( mmm[ii] ) sigma += SQR(bar[ii]-sum) ;
               sigma = mfac * sqrt( sigma/(mcount-1) ) ;
            }
            sum = mfac * sum ;
         }
         break ;
      }

      if( doall ){ sumar[iv] = sum ; sigmar[iv] = sigma ; }
   }

   free(mmm) ;

   /*-- send report --*/

   if( doall ){
      str = (char *) malloc( 1024 + 64*nvals ) ;
      sprintf(str," ****** ROI statistics ****** \n"
                  " Source  = %s [all sub-bricks] \n"
                  " Mask    = %s [%s]" ,
              DSET_FILECODE(input_dset) ,
              DSET_FILECODE(mask_dset)  , DSET_BRICK_LABEL(mask_dset,miv) ) ;
      if( mask_bot <= mask_top ){
         sprintf(buf," [range %g .. %g]" , mask_bot , mask_top ) ;
         strcat(str,buf) ;
      }
      strcat(str," \n") ;
      sprintf(buf," Count   = %d voxels\n",mcount) ; strcat(str,buf) ;
      for( iv=0 ; iv < nvals ; iv++ ){
         AV_fval_to_char( sumar[iv]  , abuf ) ;
         AV_fval_to_char( sigmar[iv] , sbuf ) ;
         sprintf(buf," Average = %9.9s  Sigma = %9.9s [%s]  \n",
                 abuf,sbuf , DSET_BRICK_LABEL(input_dset,iv) ) ;
         strcat(str,buf) ;
      }
      PLUTO_popup_textwin( plint , str ) ;

      /* 06 Aug 1998 */

      if( cname != NULL && cname[0] != '\0' ){
         MRI_IMAGE * qim = mri_new_vol_empty( nvals,1,1 , MRI_float ) ;
         mri_fix_data_pointer( sumar , qim ) ;
         PLUTO_register_timeseries( cname , qim ) ;

         if( cdisk ){                         /* 22 Aug 2000 */
            if( PLUTO_prefix_ok(cname) ){
               char * cn ;
               if( strstr(cname,".1D") == NULL ){
                  cn = malloc(strlen(cname)+8) ;
                  strcpy(cn,cname) ; strcat(cn,".1D") ;
               } else {
                  cn = cname ;
               }
               mri_write_1D( cn , qim ) ;
               if( cn != cname ) free(cn) ;
            } else {
               PLUTO_popup_transient(plint," \n"
                                           "** Illegal filename **\n"
                                           "** in 'To Disk?' !! **\n" ) ;
            }
         }

         mri_fix_data_pointer( NULL , qim ) ; mri_free(qim) ;
      }

      free(str) ; free(sumar) ; free(sigmar) ;

   } else if( mask_bot <= mask_top ){
      str = (char *) malloc( 1024 ) ;
      sprintf( str , " *** ROI Statistics *** \n"
                     " Source  = %s [%s] \n"
                     " Mask    = %s [%s] [range %g .. %g] \n"
                     " Count   = %d voxels \n"
                     " Average = %g \n"
                     " Sigma   = %g " ,
               DSET_FILECODE(input_dset) , DSET_BRICK_LABEL(input_dset,ivbot) ,
               DSET_FILECODE(mask_dset)  , DSET_BRICK_LABEL(mask_dset,miv)    ,
               mask_bot , mask_top , mcount , sum , sigma ) ;
      PLUTO_popup_message(plint,str) ;
      free(str) ;

   } else {
      str = (char *) malloc( 1024 ) ;
      sprintf( str , " *** ROI Statistics *** \n"
                     " Source  = %s [%s] \n"
                     " Mask    = %s [%s] \n"
                     " Count   = %d voxels \n"
                     " Average = %g \n"
                     " Sigma   = %g " ,
               DSET_FILECODE(input_dset) , DSET_BRICK_LABEL(input_dset,ivbot) ,
               DSET_FILECODE(mask_dset)  , DSET_BRICK_LABEL(mask_dset,miv)    ,
               mcount , sum , sigma ) ;
      PLUTO_popup_message(plint,str) ;
      free(str) ;
   }

   return NULL ;
}
