#include "mrilib.h"
#include "thd.h"

/*---------------------------------------------------------------------------
  This program catenates multiple 3D datasets in various directions.
----------------------------------------------------------------------------*/

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

static THD_3dim_dataset_array *XCAT_dsar  = NULL ;  /* input datasets */
static int                     XCAT_dir   = 1 ;     /* 1=x, 2=y, 3=z */
static int                     XCAT_nbrik = -1 ;    /* # bricks */
static int                     XCAT_verb  = 0 ;     /* verbose? */
static int                     XCAT_datum = -1 ;    /* dataset datum */

#define DSUB(id) DSET_IN_3DARR(XCAT_dsar,(id))

static char XCAT_output_prefix[THD_MAX_PREFIX] = "xyzcat" ;

/*--------------------------- prototypes ---------------------------*/

void XCAT_read_opts( int , char ** ) ;
void XCAT_Syntax(void) ;

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

void XCAT_read_opts( int argc , char *argv[] )
{
   int nopt=1 , ii , nerr=0 ;
   THD_3dim_dataset * dset ;

   INIT_3DARR(XCAT_dsar) ;  /* array of datasets */

   while( nopt < argc ){

      /**** -prefix prefix ****/

      if( strncmp(argv[nopt],"-prefix",6) == 0 ){
        if( ++nopt >= argc ) ERROR_exit("need argument after -prefix!\n") ;

        MCW_strncpy( XCAT_output_prefix , argv[nopt++] , THD_MAX_PREFIX ) ;

        if( !THD_filename_ok(XCAT_output_prefix) )
          ERROR_exit("Illegal character in -prefix '%s'",XCAT_output_prefix) ;

        continue ;
      }

      /**** -verb ****/

      if( strncmp(argv[nopt],"-verb",5) == 0 ){
        XCAT_verb++ ; nopt++ ; continue ;
      }

      /*** -dir ***/

      if( strncmp(argv[nopt],"-dir",4) == 0 ){
        nopt++ ;
        if( nopt >= argc ) ERROR_exit("need argument after -dir!") ;
        switch( toupper(argv[nopt][0]) ){
          default: ERROR_exit("don't understand '-dir %s'",argv[nopt]) ;
          case 'X': case 'I': XCAT_dir = 1 ; break ;
          case 'Y': case 'J': XCAT_dir = 2 ; break ;
          case 'Z': case 'K': XCAT_dir = 3 ; break ;
        }
        nopt++ ; continue ;
      }

      /**** Garbage of various flavors ****/

      if( strcmp(argv[nopt],"-input") == 0 ){
        nopt++ ;  /* and fall thru */
      } else if( argv[nopt][0] == '-' ){
        ERROR_exit("Unknown option: %s\n",argv[nopt]) ;
      }

      /**** read dataset ****/

      if( XCAT_verb ) INFO_message("Opening dataset %s",argv[nopt]) ;

      dset = THD_open_dataset( argv[nopt++] ) ;
      if( dset == NULL ){
        ERROR_message("Can't open dataset %s",argv[nopt-1]) ; nerr++ ; continue ;
      }
      THD_force_malloc_type( dset->dblk , DATABLOCK_MEM_MALLOC ) ;

      if( XCAT_datum < 0 ) XCAT_datum = DSET_BRICK_TYPE(dset,0) ;
      if( XCAT_nbrik < 0 ) XCAT_nbrik = DSET_NVALS(dset) ;

      /* check stuff */

      if( !DSET_datum_constant(dset) ){
        ERROR_message("Dataset %s doesn't have a constant data type :-(",argv[nopt-1]) ;
        nerr++ ;
      }
      if( DSET_NVALS(dset) != XCAT_nbrik ){
        ERROR_message("Dataset %s has different number of sub-bricks",argv[nopt-1]) ;
        nerr++ ;
      }
      if( DSET_BRICK_TYPE(dset,0) != XCAT_datum ){
        ERROR_message("Dataset %s has a different data type",argv[nopt-1]) ;
        nerr++ ;
      }
      if( THD_need_brick_factor(dset) ){
        ERROR_message("Can't catenated dataset %s that uses brick factors - sorry!",argv[nopt-1]) ;
        nerr++ ;
      }

      ADDTO_3DARR(XCAT_dsar,dset) ;  /* list of datasets */

   }  /* end of loop over command line arguments */

   if( nerr > 0 ) ERROR_exit("Can't continue after above ERROR message%s",
                             (nerr==1) ? "\0" : "s" ) ;
   return ;
}

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

void XCAT_Syntax(void)
{
   printf(
    "Usage: 3dXYZcat [options] dataset dataset ...\n"
    "* Catenates datasets spatially (for time cat-ing, cf. 3dTcat).\n"
    "* The input datasets must match, in the sense that the pieces\n"
    "  fit together properly (spatially and in time).\n"
    "* Unlike in 3dZcat, all input datasets must be stored with the\n"
    "  same data type (e.g., shorts, floats, ...); also, sub-brick scale\n"
    "  factors are not allowed.  If you need to spatially catenate scaled\n"
    "  short datasets, for example, convert them to float format using\n"
    "  '3dcalc -float', then catenate THOSE datasets.\n"
    "\n"
    "Options:\n"
    "--------\n"
    "  -prefix pname = Use 'pname' for the output dataset prefix name.\n"
    "                    [default prefix = 'xyzcat']\n"
    "  -verb         = Print out some verbositiness as the program proceeds.\n"
    "  -dir Q        = Catenate along direction 'Q', which is one of\n"
    "                    X or Y or Z (synonyms are I or J or K)\n"
    "                  which are the STORAGE directions (not DICOM) of the\n"
    "                  3D grid of the input datasets.\n"
    "                    [default direction = 'X', for no good reason]\n"
    "\n"
    "Command line arguments after the above are taken as input datasets.\n"
    "\n"
    "Notes:\n"
    "------\n"
    "* If the i-th input dataset has dimensions nx[i] X ny[i] X nz[i], then\n"
    "    case Q = X | I ==> all ny[i] and nz[i] must be the same;\n"
    "                       the output dataset has nx = sum{ nx[i] }\n"
    "    case Q = Y | J ==> all nx[i] and nz[i] must be the same;\n"
    "                       the output dataset has ny = sum{ ny[i] }\n"
    "    case Q = Z | K ==> all nx[i] and ny[i] must be the same;\n"
    "                       the output dataset has nz = sum{ nz[i] }\n"
    "* In all cases, the input datasets must have the same number of\n"
    "  sub-bricks (time points) and the same data storage type.\n"
    "* You can use the '3dinfo' program to see the orientation and\n"
    "    grid size of a dataset, to help you decide how to glue your\n"
    "    inputs together.\n"
    "* There must be at least two datasets input (otherwise, the\n"
    "    program doesn't make much sense, now does it?).\n"
    "* This is mostly useful for making side-by-side pictures from\n"
    "    multiple datasets, for edification and elucidation.\n"
    "* If you have some other use for 3dXYZcat, let me know!\n"
    "\n"
    "** Author: RW Cox [Dec 2010] **\n"
   ) ;

   PRINT_COMPILE_DATE ; exit(0) ;
}

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

int main( int argc , char *argv[] )
{
   int ninp , ids , iv ,kz,new_nz, nx,ny,nz,nxy,nxyz ;
   THD_3dim_dataset *new_dset=NULL , *dset=NULL ;
   MRI_IMAGE *outim;
   MRI_IMARR *im_array;

   /*** read input options ***/

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

   /*-- addto the arglist, if user wants to --*/

   { int new_argc ; char ** new_argv ;
     addto_args( argc , argv , &new_argc, &new_argv ) ;
     if( new_argv != NULL ){ argc = new_argc ; argv = new_argv ; }
   }

   mainENTRY("3dXYZcat main") ; machdep() ; AFNI_logger("3dXYZcat",argc,argv) ;
   PRINT_VERSION("3dXYZcat") ;

   XCAT_read_opts( argc , argv ) ;

   /*** create new dataset (empty) ***/

   ninp = XCAT_dsar->num ;
   if( ninp < 2 )
     ERROR_exit("Must have at least 2 input datasets!") ;

   for( ids=0 ; ids < ninp ; ids++ ){
     dset = DSUB(ids) ; if( DSET_TIMESTEP(dset) > 0.0f ) break ;
   }
   if( ids == ninp ) dset = DSUB(0) ; /* fallback position */
   if( XCAT_verb )
     INFO_message("Using %s as 'master' dataset",DSET_HEADNAME(dset)) ;

   new_dset = EDIT_empty_copy( dset ) ; /* make a copy of its header */

   tross_Copy_History( dset , new_dset ) ;
   tross_Make_History( "3dXYZcat" , argc,argv , new_dset ) ;

   EDIT_dset_items( new_dset ,
                      ADN_prefix    , XCAT_output_prefix ,
                      ADN_datum_all , XCAT_datum ,
                      ADN_nsl       , 0 , /* kill time offsets  */
                    ADN_none ) ;

   /* can't re-write existing dataset */

   if( THD_deathcon() && THD_is_file(DSET_HEADNAME(new_dset)) )
     ERROR_exit("Output dataset %s already exists!",DSET_HEADNAME(new_dset) ) ;

   THD_force_malloc_type( new_dset->dblk , DATABLOCK_MEM_MALLOC ) ;

   if( XCAT_verb ) INFO_message("Loading input datasets") ;
   for( ids=0 ; ids < ninp ; ids++ ){
     dset = DSUB(ids) ; DSET_load(dset) ; CHECK_LOAD_ERROR(dset) ;
   }

   /*** Loop over output sub-bricks ***/

   for( iv=0 ; iv < DSET_NVALS(new_dset) ; iv++ ){

      if( XCAT_verb ) ININFO_message("Computing output sub-brick #%d",iv) ;

      INIT_IMARR(im_array) ;
      for( ids=0 ; ids < ninp ; ids++ )
        ADDTO_IMARR( im_array , DSET_BRICK(DSUB(ids),iv) ) ;

      outim = mri_catvol_1D(im_array,XCAT_dir) ;
      if( outim == NULL )
        ERROR_exit("Can't catenate! Dataset dimensions mismatch? :-(") ;

      FREE_IMARR(im_array) ;
      for( ids=0 ; ids < ninp ; ids++ ) DSET_unload_one(DSUB(ids),iv) ;

      if( iv == 0 ){  /* mangle sub-brick xyz dimensions */
        THD_ivec3 iv_nxyz ;
        LOAD_IVEC3( iv_nxyz , outim->nx , outim->ny , outim->nz ) ;
        EDIT_dset_items( new_dset , ADN_nxyz , iv_nxyz , ADN_none ) ;
      }

      EDIT_substitute_brick( new_dset,iv, XCAT_datum,mri_data_pointer(outim) ) ;
      mri_clear_data_pointer(outim) ; mri_free(outim) ;

   } /* end of loop over output sub-bricks */

   /*-- write dataset --*/

   for( ids=0 ; ids < ninp ; ids++ ) DSET_delete( DSUB(ids) ) ;
   DSET_write(new_dset) ;
   WROTE_DSET(new_dset) ;
   exit(0) ;
}
