#include "mrilib.h"

#define FatalError(str) \
   ( fprintf(stderr,"\nEPsim error: %s\n",(str)) , exit(1) )

static int use_shm    = 1 ;
static int delay      = 100 ;
static int use_child  = 0 ;
static int use_3T     = 0 ;
char *     fname_3T   = NULL ;
static int ntimes     = 1 ;
static char host[128] = "localhost" ;
static char buf[4096] ;
static int  nbytes , jj , first=1 ;

#if 0 /*  ZSS June 2011. Delete useless code after dust has settled.  */
   #define CONTROL_PORT 7954
   #define TCP_PORT     7955
   /* replace with:
      get_port_named("CONTROL_PORT")
      get_port_named("AFNI_PLUGOUT_TCP_BASE") 
   */
#endif
#define SHM_NAME     "shm:epsim:1M"

static IOCHAN * ioc = NULL ;

#define SHORT_DELAY      1            /* msec */
#define LONG_DELAY      10

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

void Syntax(void)
{
   printf(
    "Program:   epsim \n\n"
    "Purpose:   Extract 2D data files from 3D dataset & send to AFNI\n"
    "Usage:     epsim [-v] [-shm | -tcp] [-delay mmm] [-host cpu] [-child]\n"
    "                 -input fname \n"
    "Options: 	\n"
    "-v                 Print out verbose information.\n"
#if 0
    "-nsize             Adjust size of 2d data file to be NxN, by padding \n"
    "                     with zeros, where N is a power of 2. \n"
#endif
    "-shm               Use shared memory to communicate with AFNI. \n"
    "-tcp               Use TCP/IP to communicate with AFNI. \n"
    "-delay mmm         Delay mmm milliseconds between slices. \n"
    "-host cpu          Access AFNI on host named 'cpu'. \n"
    "-child             Tell AFNI to read header info from a child. \n"
    "-3T fname          Tell AFNI to get data from the 3T_toafni program\n"
    "                     with the 3T control data stored in file 'fname'.\n"
    "-input fname       Read from dataset in file 'fname'. \n"
    "-times n           Send the dataset 'n' times.\n"
   ) ;
   exit(0) ;
}

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

void F3D_initialize_user_data ( int Argc, char * Argv[],
   RwcBoolean * verbose, RwcBoolean * nsize,
   int * zfirst, int * zlast, int * tfirst, int * tlast,
   char * input_filename, char * prefix_filename )
   
{
   const int BIGNUMBER = 10000;
   int nopt;
   float ftemp;

   if (Argc < 2)  Syntax();

   /* --- set default values --- */
   *verbose = FALSE;
   *nsize = FALSE;
   *zfirst = 1;
   *zlast = BIGNUMBER;
   *tfirst = 1;
   *tlast = BIGNUMBER;
   strcpy(input_filename, "");
   strcpy(prefix_filename, "");

   
   /* --- scan options --- */
   nopt = 1 ; 
   while ( nopt < Argc && Argv[nopt][0] == '-' )
   {
     
       /* --- help option --- */
      if ( strncmp(Argv[nopt],"-help",4) == 0 )  Syntax() ;

      /* --- verbose option --- */
      if ( strncmp(Argv[nopt],"-v",2) == 0 )  
      {  
         *verbose = TRUE;
         nopt++ ;
         continue;
      }
#if 0
      /* --- nsize option --- */
      if ( strncmp(Argv[nopt],"-nsize",4) == 0 ) 
      {
         *nsize = TRUE;
         nopt++ ;
         continue;
      }
#endif

      /*-- -times n --*/
      if( strncmp(Argv[nopt],"-times",4) == 0 ){
         if( ++nopt >= Argc ) FatalError("-times needs an argument") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         if( ftemp >= 1.0 ) ntimes = (int) ftemp ;
         nopt++ ; continue ;   
      }

#if 0
      /* --- zfirst option --- */
      if ( strncmp(Argv[nopt],"-zfirst",4) == 0 )
      {
         if( ++nopt >= Argc ) FatalError("-zfirst needs an argument") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         *zfirst = (int) ftemp ;
         nopt++ ;
         continue ;   
      }

      /* --- zlast option --- */
      if ( strncmp(Argv[nopt],"-zlast",4) == 0 )
      {
         if( ++nopt >= Argc ) FatalError("-zlast needs an argument") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         *zlast = (int) ftemp ;
         nopt++ ;
         continue ;
      }

      /* --- tfirst option --- */ 
      if ( strncmp(Argv[nopt],"-tfirst",4) == 0 )
      {
         if( ++nopt >= Argc ) FatalError("-tfirst needs an argument") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         *tfirst = (int) ftemp ;
         nopt++ ;
         continue ;
      }
       
      /* --- tlast option --- */
      if ( strncmp(Argv[nopt],"-tlast",4) == 0 )
      {
         if( ++nopt >= Argc ) FatalError("-tlast needs an argument") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         *tlast = (int) ftemp ;
         nopt++ ;
         continue ;
      }
#endif

      if( strncmp(Argv[nopt],"-shm",4) == 0 ){
         use_shm = 1 ;
         nopt++ ; continue ;
      }

      if( strncmp(Argv[nopt],"-tcp",4) == 0 ){
         use_shm = 0 ;
         nopt++ ; continue ;
      }

      if( strncmp(Argv[nopt],"-child",4) == 0 ){
         use_child = 1 ;
         nopt++ ; continue ;
      }

      if( strncmp(Argv[nopt],"-3T",4) == 0 ){
         if( ++nopt >= Argc ) FatalError("-3T needs a filename") ;
         fname_3T = Argv[nopt] ;
         use_3T = 1 ;
         nopt++ ; continue ;
      }

      if( strncmp(Argv[nopt],"-delay",4) == 0 ){
         if ( ++nopt >= Argc ) FatalError("-delay needs a number") ;
         ftemp = strtod( Argv[nopt] , NULL ) ;
         if( ftemp >= 0.0 ) delay = (int) ftemp ;
         nopt++ ; continue ;
      }

      if( strncmp(Argv[nopt],"-host",4) == 0 ){
         if ( ++nopt >= Argc ) FatalError("-host needs a cpu name") ;
         strcpy( host , Argv[nopt] ) ;
         nopt++ ; continue ;
      }
    
      /* --- input file name --- */
      if ( strncmp(Argv[nopt],"-input",4) == 0 )
      {
         if ( ++nopt >= Argc ) FatalError("-input needs a name") ;
         strcpy ( input_filename , Argv[nopt] ) ;
         nopt++ ; continue ;
      }

#if 0      
      /* --- prefix name --- */
      if ( strncmp(Argv[nopt],"-prefix",4) == 0 )
      {
         if ( ++nopt >= Argc ) FatalError("-prefix needs a name") ;
         strcpy ( prefix_filename , Argv[nopt] ) ;
         nopt++ ; continue ;
      }
#endif

      /* --- exception --- */
      fprintf(stderr,"Don't understand argument %s\n",Argv[nopt]) ;
      FatalError ("Illegal input");
      
   }  /* nopt */
   
   /* --- check for valid inputs --- */
#if 0
   if (*zfirst > *zlast)   
      FatalError ("Cannot have zfirst > zlast");
   if (*tfirst > *tlast)   
      FatalError ("Cannot have tfirst > tlast");
   if (!strcmp(prefix_filename,"")) 
      FatalError ("Must specify prefix file name.");
#endif
   if (!strcmp(input_filename,"")) 
      FatalError ("Must specify input file name. ");

   if( strcmp(host,"localhost") != 0 && use_shm ){
      printf("EPsim: must use TCP/IP for host %s\n",host) ;
      use_shm = 0 ;
   }

   if( use_child && use_3T )
      FatalError("Can't use -child and -3T together!") ;

   return;
}

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

int main( int argc , char * argv[] )
{
   /* --- variable declarations --- */
   THD_3dim_dataset * dset ;
   THD_diskptr * dskptr;
   int nx, ny, nz, nv, itim;
   RwcBoolean verbose, nsize;
   int ok;
   MRI_IMAGE * im, * im2d, * tim2d;
   MRI_TYPE kind;
   int ibr, iz, count, izz,izsub ;
   int zfirst, zlast, tfirst, tlast;
   char input_filename[THD_MAX_NAME],
        prefix_filename[THD_MAX_NAME], 
        output_filename[THD_MAX_NAME],
        str[THD_MAX_NAME];

   /* --- get user command line inputs --- */
   F3D_initialize_user_data (argc, argv, 
      &verbose, &nsize,
      &zfirst, &zlast, &tfirst, &tlast,
      input_filename, prefix_filename );
      
   /* --- open 3d data set --- */
   dset = THD_open_one_dataset( input_filename ) ;
   if( dset == NULL )  FatalError ("Unable to open input file") ;
   if ( verbose )  printf("EPsim: 3d Dataset File = %s\n" , input_filename ) ; 
      
   /* --- load data block --- */
   ok = THD_load_datablock( dset->dblk );
   if ( !ok )  FatalError ("Unable to load data block") ;

   /* --- get data dimensions --- */
   dskptr = dset->dblk->diskptr;
   nx = dskptr->dimsizes[0];
   ny = dskptr->dimsizes[1];
   nz = dskptr->dimsizes[2];
   nv = dskptr->nvals;
   if ( verbose )  
      printf ("EPsim: nx=%d  ny=%d  nz=%d  nv=%d\n",   nx, ny, nz, nv);

   /* --- check for valid user inputs --- */
   if (zfirst < 1) zfirst = 1;
   if (zlast > nz) zlast = nz;
   if (tfirst < 1) tfirst = 1;
   if (tlast > nv) tlast = nv;
   if (zfirst > nz)  FatalError ("No data selected -- zfirst too large.");
   if (zlast < 1)    FatalError ("No data selected -- zlast too small.");
   if (tfirst > nv)  FatalError ("No data selected -- tfirst too large.");
   if (tlast < 1)    FatalError ("No data selected -- tlast too small.");    

   /* --- get data type --- */
   kind = IMAGE_IN_IMARR ( dset->dblk->brick, 0 ) -> kind;
   if ( verbose )  printf ("EPsim: datum = %s \n", MRI_TYPE_name[kind]);

   /* --- create 2d data pointer --- */
   im2d = mri_new_vol_empty ( nx, ny, 1, kind );

   /*** open channel to AFNI ***/

   sprintf( buf , "tcp:%s:%d" , host , get_port_named("CONTROL_PORT") ) ;
   ioc = iochan_init( buf , "create" ) ;
   if( ioc == NULL ) FatalError("Cannot open control channel to AFNI") ;

   if( verbose ) printf("EPsim: waiting for AFNI") ; fflush(stdout) ;

   while(1){
      iz = iochan_goodcheck( ioc , 1000 ) ;
      if( iz < 0 ) FatalError("control channel failed") ;
      if( iz > 0 ) break ;
      if( verbose ){ printf(".") ; fflush(stdout) ; }
   }
   if( verbose ){ printf("!\n") ; fflush(stdout) ; }

   if( use_shm ) strcpy( buf , SHM_NAME ) ;
   else          sprintf(buf , "tcp:%s:%d" , 
                         host , get_port_named("AFNI_PLUGOUT_TCP_BASE") ) ;

   if( use_child ){
      jj = strlen(buf) ;
      sprintf(buf+jj,"\ncat epsim.out") ;
   } else if( use_3T ){
      jj = strlen(buf) ;
      sprintf(buf+jj,"\n3T_toafni -dummy < %s" , fname_3T ) ;
   }

   if( verbose ) printf("sending control data: %s\n",buf) ;

   jj = iochan_sendall( ioc , buf , strlen(buf)+1 ) ;
   if( jj < 0 ) FatalError("send control data failed") ;
   iochan_sleep(LONG_DELAY) ;                      /* wait a bit */
   while( ! iochan_clearcheck(ioc,LONG_DELAY) )    /* loop until cleared */
      iochan_sleep(LONG_DELAY) ;

   if( verbose ) printf("EPsim: closing control channel\n") ;
   IOCHAN_CLOSE(ioc) ;

   /*** now open data channel ***/

   ioc = iochan_init( buf , "create" ) ;
   if( ioc == NULL ) FatalError("Cannot open data channel to AFNI") ;

   if( verbose ) printf("EPsim: waiting for AFNI") ; fflush(stdout) ;

   while(1){
      iz = iochan_goodcheck( ioc , 1000 ) ;
      if( iz < 0 ) FatalError("data channel failed") ;
      if( iz > 0 ) break ;
      if( verbose ){ printf(".") ; fflush(stdout) ; }
   }
   if( verbose ){ printf("!\n") ; fflush(stdout) ; }

   if( use_child ){
      FILE * fp = fopen( "epsim.out" , "w" ) ;
      if( fp == NULL ){fprintf(stderr,"Can't open epsim.out!\n");IOCHAN_CLOSE(ioc);exit(1);}
      fprintf( fp ,  "ZNUM %d\n"
                     "ZDELTA %g\n"
                     "XYFOV %g %g\n"
                     "ZFIRST %g%c\n"
                     "ZORDER seq\n"
                     "XYZAXES %s %s %s\n"
                     "ACQUISITION_TYPE %s\n"
                   ,
                     nz ,
                     fabs(dset->daxes->zzdel) ,
                     fabs(dset->daxes->xxdel)*nx , fabs(dset->daxes->yydel)*ny ,
                     fabs(dset->daxes->zzorg) , ORIENT_first[dset->daxes->zzorient] ,
                     ORIENT_shortstr[dset->daxes->xxorient] ,
                       ORIENT_shortstr[dset->daxes->yyorient] ,
                       ORIENT_shortstr[dset->daxes->zzorient] ,
                     ( ((zlast-zfirst)>0) ? "2D+zt" : "2D+z" )
                   ) ;
      fclose(fp) ;
      if( verbose ) printf("EPsim: wrote epsim.out file\n") ;

      sprintf( buf , "XYMATRIX %d %d\n"
                     "DATUM %s\n"
                   ,
                     nx , ny ,
                     MRI_TYPE_name[kind]
                   ) ;
   } else if( use_3T ){
      sprintf( buf , "XYMATRIX %d %d\n"
                     "DATUM %s\n"
                   ,
                     nx , ny ,
                     MRI_TYPE_name[kind]
                   ) ;
   } else {
      sprintf( buf , "ZNUM %d\n"
                     "ZDELTA %g\n"
                     "XYFOV %g %g\n"
                     "ZFIRST %g%c\n"
                     "ZORDER seq\n"
                     "XYZAXES %s %s %s\n"
                     "ACQUISITION_TYPE %s\n"
                     "XYMATRIX %d %d\n"
                     "DATUM %s\n"
                   ,
                     nz ,
                     fabs(dset->daxes->zzdel) ,
                     fabs(dset->daxes->xxdel)*nx , fabs(dset->daxes->yydel)*ny ,
                     fabs(dset->daxes->zzorg) , ORIENT_first[dset->daxes->zzorient] ,
                     ORIENT_shortstr[dset->daxes->xxorient] ,
                       ORIENT_shortstr[dset->daxes->yyorient] ,
                       ORIENT_shortstr[dset->daxes->zzorient] ,
                     ( ((zlast-zfirst)>0) ? "2D+zt" : "2D+z" ) ,
                     nx , ny ,
                     MRI_TYPE_name[kind]
                   ) ;
   }

   nbytes = im2d->nvox * im2d->pixel_size ;

   for( itim=0 ; itim < ntimes ; itim++ ){
   count = 0;

   if( use_3T ) izsub = (nz%2 == 0) ? (nz-1) : (nz) ;

   for ( ibr = tfirst-1 ; ibr < tlast ; ibr++ )
   {
      for ( iz = zfirst-1 ; iz < zlast ; iz++ )
      {
         /* --- set 2d data pointer into 3d data set --- */
         im = IMAGE_IN_IMARR ( dset->dblk->brick, ibr ); 

         if( use_3T ){
           izz = 2*iz ; if( izz >= nz ) izz -= izsub ;  /* alt ordering */
         } else {
           izz = iz ;                                   /* seq ordering */
         }

         switch ( kind )
         {
            case MRI_byte :
               mri_set_data_pointer(im2d, MRI_BYTE_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_short :
               mri_set_data_pointer(im2d, MRI_SHORT_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_int :
               mri_set_data_pointer(im2d, MRI_INT_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_float :
               mri_set_data_pointer(im2d, MRI_FLOAT_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_double :
               mri_set_data_pointer(im2d, MRI_DOUBLE_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_complex :
               mri_set_data_pointer(im2d, MRI_COMPLEX_PTR(im) + iz*nx*ny) ;
            break;
            case MRI_rgb :
               mri_set_data_pointer(im2d, MRI_RGB_PTR(im) + 3*iz*nx*ny) ;
            break;
            default :
               FatalError ("Illegal data type encountered.");
         } 

#if 0
         /* --- create 2d data file name --- */
         strcpy ( output_filename, prefix_filename );
         if ( nv > 1 )  
            sprintf ( str, "%02d.%04d", izz+1, ibr+1 );
         else
            if ( nz > 999 )
               sprintf ( str, ".%04d", izz+1 );
            else
               sprintf ( str, ".%03d", izz+1 );
         strcat ( output_filename, str );
#endif

         if( first ){
            if( verbose )
               printf("EPsim: sending this data as header info in image channel:\n%s\n",buf) ;
            jj = iochan_sendall( ioc , buf , strlen(buf)+1 ) ;
            if( jj < 0 ) FatalError("send header info failed") ;
            first = 0 ;
         }
 
         if ( verbose )
            printf ( "EPsim: sending 2D image izz=%d ibr=%d\n", izz,ibr ); 

         jj = iochan_sendall( ioc , mri_data_pointer(im2d) , nbytes ) ;
         if( jj < 0 ) FatalError("send image failed") ;
         iochan_sleep( delay ) ;

#if 0
         if ( !nsize )
            ok = mri_write ( output_filename, im2d );
         else
         {
            tim2d = mri_nsize (im2d);
            ok = mri_write ( output_filename, tim2d);
            mri_free (tim2d);
         }
#endif
         
         count ++ ;

      }  /* --- iz --- */
   }  /* --- ibr --- */ 
   sleep(20) ;
   } /* -- itim --*/

   if ( verbose )  printf ("Sent %d 2D images. \n", count);

   if( verbose ){ printf("Waiting for AFNI") ; fflush(stdout) ; }
   while(1){
      jj = iochan_clearcheck(ioc,1000) ;
      if( jj ) break ;
      if( verbose ){ printf(".") ; fflush(stdout) ; }
   }
   if( verbose ) printf("!\n") ;
   iochan_sleep(100) ; IOCHAN_CLOSE(ioc) ;

   exit(0) ;
}
