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

/*---------------------------------------------------------------------------*/
/*!  Routine to substitute one brick array for another in a 3D dataset.
     - dset  = dataset to be edited
     - ival  = index of sub-brick to be replaced
     - ftype = datum type of substitute array
     - fim   = array of substitute data -- it may be NULL.  If it is not NULL,
               then should contain mri_datum_size(ftype) * nxx*nyy*nzz bytes.
              * If it is NULL, then space will be calloc-ed.
              * Do NOT free this memory after this function - the
                data in the dataset IS this memory, not a copy of it!

   Notes:
     - The original brick (an MRI_IMAGE within an MRI_IMARR) is deleted
       from memory.  A new brick is put in its place.
     - This can only be done on a brick that is malloc-ed, not mmap-ed!
     - The brick_bytes and total_bytes field of the datablock are
       patched, but the brick_fac field is NOT changed here.
-----------------------------------------------------------------------------*/

void EDIT_substitute_brick(THD_3dim_dataset *dset, int ival, int ftype,void *fim)
{
   THD_datablock *dblk ;
   MRI_IMAGE *newim , *oldim ;
   int nullfim = (fim==NULL) ;
   int64_t nbytes ;

ENTRY("EDIT_substitute_brick") ;

   /**-- Sanity Checks --**/

   if( ! ISVALID_3DIM_DATASET(dset) )                   EXRETURN; /* error! */
   if( dset->dblk->brick == NULL )                      EXRETURN; /* ditto! */
   if( dset->dblk->malloc_type != DATABLOCK_MEM_MALLOC )EXRETURN; /* ditto! */
   if( ival >= dset->dblk->nvals || ival < 0 )          EXRETURN; /* ditto! */
   if( ftype < 0 || ftype > LAST_MRI_TYPE )             EXRETURN; /* ditto! */

   oldim = DSET_BRICK(dset,ival) ; if( oldim == NULL )  EXRETURN; /* ditto! */

   newim  = mri_empty_conforming( oldim , ftype ) ;      /* new sub-brick */
   nbytes = (int64_t)newim->nvox
               * (int64_t)newim->pixel_size ;            /* how big it is */
   mri_free( oldim ) ;                                   /* kill old one  */

   if( nullfim ){                                        /* if needed, */
     fim = calloc( 1,nbytes ) ;                          /* make array */
     if( fim == NULL )
       ERROR_exit("malloc (out of memory) error for dataset sub-brick #%d",ival) ;
   }
   mri_fix_data_pointer( fim , newim ) ;                 /* attach new data */
   DSET_BRICK(dset,ival) = newim ;                       /* put in dataset  */

   /** change the byte count for this sub-brick and the total dataset **/

   dset->dblk->total_bytes      += (nbytes - dset->dblk->brick_bytes[ival]) ;
   dset->dblk->brick_bytes[ival] = nbytes ;

   DSET_CRUSH_BSTAT(dset,ival) ;

   THD_patch_dxyz_one(dset,ival) ;  /* 05 Jun 2007 */

   EXRETURN ;
}

/*---------------------------------------------------------------------------*/
/*! Similar to EDIT_substitute_brick(), but allows the data type to be
    converted/scaled.  The brick_fac field will be set.  [18 Sep 2006]

    Free fim after the function returns UNLESS ftype == stype
-----------------------------------------------------------------------------*/

void EDIT_substscale_brick(THD_3dim_dataset *dset, int ival,
                           int ftype,void *fim , int stype,float fac )
{
   float *far ;
   double *dar;
   int ii,nvox ;

ENTRY("EDIT_substscale_brick") ;

   /**-- Sanity Checks --**/

   if( ! ISVALID_3DIM_DATASET(dset) )                   EXRETURN; /* error! */
   if( dset->dblk->brick == NULL )                      EXRETURN; /* ditto! */
   if( dset->dblk->malloc_type != DATABLOCK_MEM_MALLOC )EXRETURN; /* ditto! */
   if( ival >= dset->dblk->nvals || ival < 0 )          EXRETURN; /* ditto! */
   if( ftype < 0 || ftype > LAST_MRI_TYPE )             EXRETURN; /* ditto! */

   /** the trivial operation? **/

   if( stype < 0 || stype > LAST_MRI_TYPE || stype == ftype ){
     EDIT_substitute_brick( dset,ival,ftype,fim ) ;
     EDIT_BRICK_FACTOR( dset,ival,0.0f ) ;
     EXRETURN ;
   }
   if( fim == NULL ){
     EDIT_substitute_brick( dset,ival,stype,NULL ) ;
     EDIT_BRICK_FACTOR( dset,ival,0.0f ) ;
     EXRETURN ;
   }

   /** at this time, can only scale float inputs to shorts or bytes **/

   if( ftype != MRI_float && ftype != MRI_double ){ /* ZSS Dec 2010 */
     ERROR_message("EDIT_substscale_brick: non-float and non-double input! "
                   "(%d %d %d)", ftype, stype, MRI_short);
     EXRETURN;
   }
   if( stype != MRI_short && stype != MRI_byte ){
     ERROR_message("EDIT_substscale_brick: non-short/byte output!"); EXRETURN;
   }

   if (ftype == MRI_float) {
      far = (float *)fim ; nvox = DSET_NVOX(dset) ;

      /** compute factor, if not supplied by user **/

      if( fac <= 0.0f ){
        float bot,top , abot,atop,mmm ; int isin ;
        bot = top = far[0] ;
        for( ii=1 ; ii < nvox ; ii++ ){
               if( far[ii] < bot ) bot = far[ii] ;
          else if( far[ii] > top ) top = far[ii] ;
        }
        abot = fabsf(bot); atop = fabsf(top); mmm = MAX(abot,atop);
        if( mmm == 0.0f ){  /** data values are all zero! **/
          fac = 1.0f ;
        } else if( stype == MRI_short ){
          isin = is_integral_data( nvox , MRI_float , far ) ;
          fac = (isin && mmm <= 32767.0f) ? 1.0f : 32767.0f / mmm ;
        } else if( stype == MRI_byte ){
          if( bot < 0.0f ){
            for( ii=0 ; ii < nvox ; ii++ ) if( far[ii] < 0.0f ) far[ii] = 0.0f ;
          }
          if( top > 0.0f ){
            isin = is_integral_data( nvox , MRI_float , far ) ;
            fac = (isin && top <= 255.0f) ? 1.0f : 255.0f / top ;
          } else {
            WARNING_message("EDIT_substscale_brick: "
                            "no positive data for -> byte");
            fac = 1.0f ;
          }
        }
      }

      /*-- now do the scaling and substitution --*/

      if( stype == MRI_short ){

        short *sar = (short *)malloc(sizeof(short)*nvox) ;
        float val ;
        if( fac == 1.0f ){
          STATUS("storing to shorts") ;
          for( ii=0 ; ii < nvox ; ii++ ){
            sar[ii] = SHORTIZE(far[ii]) ;
          }
        } else {
          STATUS("scaling to shorts") ;
          for( ii=0 ; ii < nvox ; ii++ ){
            val = fac*far[ii] ; sar[ii] = SHORTIZE(val) ;
          }
        }
        STATUS("putting into dataset") ;
        EDIT_substitute_brick( dset,ival,MRI_short,sar ) ;
        fac = (fac==1.0f) ? 0.0f : 1.0f/fac ;
        STATUS("setting scale factor") ;
        EDIT_BRICK_FACTOR( dset,ival,fac ) ;

        EDIT_misfit_report( DSET_FILECODE(dset), ival,
                            nvox , fac , sar , far  ) ;

      } else if( stype == MRI_byte ){

        byte *bar = (byte *)malloc(sizeof(byte)*nvox) ;
        float val ;
        if( fac == 1.0f ){
          for( ii=0 ; ii < nvox ; ii++ ) bar[ii] = BYTEIZE(far[ii]) ;
        } else {
          for( ii=0 ; ii < nvox ; ii++ ){
            val = fac*far[ii] ; bar[ii] = BYTEIZE(val) ;
          }
        }
        EDIT_substitute_brick( dset,ival,MRI_byte,bar ) ;
        fac = (fac==1.0f) ? 0.0f : 1.0f/fac ;
        EDIT_BRICK_FACTOR( dset,ival,fac ) ;
      }
   } else if (ftype == MRI_double) {   /* ZSS Dec 2010 */
      dar = (double *)fim ; nvox = DSET_NVOX(dset) ;

      /** compute factor, if not supplied by user **/

      if( fac <= 0.0f ){
        double bot,top , abot,atop,mmm ; int isin ;
        bot = top = dar[0] ;
        for( ii=1 ; ii < nvox ; ii++ ){
               if( dar[ii] < bot ) bot = dar[ii] ;
          else if( dar[ii] > top ) top = dar[ii] ;
        }
        abot = fabs(bot); atop = fabs(top); mmm = MAX(abot,atop);
        if( mmm == 0.0f ){  /** data values are all zero! **/
          fac = 1.0f ;
        } else if( stype == MRI_short ){
          isin = is_integral_data( nvox , MRI_double , dar ) ;
          fac = (isin && mmm <= 32767.0f) ? 1.0f : 32767.0f / mmm ;
        } else if( stype == MRI_byte ){
          if( bot < 0.0f ){
            for( ii=0 ; ii < nvox ; ii++ ) if( dar[ii] < 0.0f ) dar[ii] = 0.0f ;
          }
          if( top > 0.0f ){
            isin = is_integral_data( nvox , MRI_double , dar ) ;
            fac = (isin && top <= 255.0f) ? 1.0f : 255.0f / top ;
          } else {
            WARNING_message("EDIT_substscale_brick: "
                            "no positive data for -> byte");
            fac = 1.0f ;
          }
        }
      }

      /*-- now do the scaling and substitution --*/

      if( stype == MRI_short ){

        short *sar = (short *)malloc(sizeof(short)*nvox) ;
        double val ;
        if( fac == 1.0f ){
          STATUS("storing to shorts") ;
          for( ii=0 ; ii < nvox ; ii++ ){
            sar[ii] = SHORTIZE(dar[ii]) ;
          }
        } else {
          STATUS("scaling to shorts") ;
          for( ii=0 ; ii < nvox ; ii++ ){
            val = fac*dar[ii] ; sar[ii] = SHORTIZE(val) ;
          }
        }
        STATUS("putting into dataset") ;
        EDIT_substitute_brick( dset,ival,MRI_short,sar ) ;
        fac = (fac==1.0f) ? 0.0f : 1.0f/fac ;
        STATUS("setting scale factor") ;
        EDIT_BRICK_FACTOR( dset,ival,fac ) ;

        /* Skip the misfit report. None exists for double
        input at the moment.
         EDIT_misfit_report( ) ;*/

      } else if( stype == MRI_byte ){

        byte *bar = (byte *)malloc(sizeof(byte)*nvox) ;
        double val ;
        if( fac == 1.0f ){
          for( ii=0 ; ii < nvox ; ii++ ) bar[ii] = BYTEIZE(dar[ii]) ;
        } else {
          for( ii=0 ; ii < nvox ; ii++ ){
            val = fac*dar[ii] ; bar[ii] = BYTEIZE(val) ;
          }
        }
        EDIT_substitute_brick( dset,ival,MRI_byte,bar ) ;
        fac = (fac==1.0f) ? 0.0f : 1.0f/fac ;
        EDIT_BRICK_FACTOR( dset,ival,fac ) ;
      }
   }

   EXRETURN ;
}

/*---------------------------------------------------------------------------*/
static char EDIT_index_prefix = '#' ;
void EDIT_set_index_prefix(char c){ EDIT_index_prefix = c ;
#if 0
  INFO_message("EDIT_set_index_prefix('%c') --> %c",c,EDIT_get_index_prefix() ) ;
#endif
   } /* 11 Jul 2019 */

char EDIT_get_index_prefix(void)  { return EDIT_index_prefix ; } /* 11 Jul 2019 */
/*---------------------------------------------------------------------------*/
