#!/bin/tcsh -e

# !!! comment about movie/mpeg stuff! gifsicle, etc.

# !!!? control loop speed?

# written by PA Taylor (NIMH, NIH)
# filter sets of acquired DWIs with accompanying bvals/vecs

# --------------------- revision history -------------------------
#
# Jan, 2017
#   + rename
#
# Jan 27, 2017
#   + new opts
#   + switch to 1dDW_Grad_o_Mat++
#
#set version = "2.5"
#set rev_dat = "Feb 20, 2017"

#   + add snapshotting with @djunct_*_imager
#
#set version = "2.6"; set rev_dat = "Feb 23, 2017"
#   + add movie-making with @djunct_*_imager
#
#set version = "2.7"; set rev_dat = "Apr 17, 2017"
#   + I/O standardization
#   + cut back to just a single directory at once.  
#     -> Simplifies life (for everyone, really, it's not just 
#        laziness by the author (well, only *partially* the latter))
#
#set version = "2.8"; set rev_dat = "Apr 19, 2017"
#   + more I/O work
#   + more general outputting, when scaling/not scaling
#
#set version = "2.8"; set rev_dat = "Apr 19, 2017"
#   + changed input notation
#   + fixed help example
#
#set version = "2.9"; set rev_dat = "June 2, 2017"
#   + changed output mat/vec/bval extensions from 'txt' -> 'dat';
#     should me more uniform with other fat* funcs
#
#set version = "3.0"; set rev_dat = "July 27, 2017"
#   + fixed some I/O issues with rowvecs and separate bvals
#
set version = "3.1"; set rev_dat = "Sep 04, 2017"
#   + work with new @chauffeur*, -prefix only
#   + update help file format for more autoprompting of opts
#
set version = "3.2"; set rev_dat = "Jan 09, 2019"
#   + new -select_file option
#   + whine if *no* selection string entered
#
# ----------------------------------------------------------------
 
set this_prog = "fat_proc_filter_dwis" 
set tpname    = "${this_prog:gas/fat_proc_//}"
set here      = $PWD

# ----------------- find AFNI and set viewer ---------------------

# find AFNI binaries directory and viewer location
set adir      = ""
which afni >& /dev/null
if ( $status ) then
    echo "** Cannot find 'afni' (?!)."
    goto BAD_EXIT
else
    set aa   = `which afni`
    set adir = $aa:h
endif

# default location of viewer: user could modify!
set my_viewer    = "$adir/@chauffeur_afni"
set my_viewer_4d = "$adir/@djunct_4d_imager"

# ----------------------- set defaults --------------------------

set idwi     = ""                 # necessary input: DWI dset
set invecmat = ( "" "" "" "" )    # switches+names for bvecs and bvals
set outvecmat = ( "" "" "" "" )    # switches+names for bvecs and bvals
set do_unit_mag = ""
set opref    = ""
set odir     = ""
set KEEP     = "" 
set KEEPfile = "" 
set KEEP3    = ""
set ll2       = "{"               # for column selection (def guess)
set rr2       = "}"               # for column selection (def guess)
set ll4       = ""                # empty
set rr4       = ""                # empty

set DO_VIEWER = "1"
set movie     = ""
set output_cmd = 1               # def: output copy of this command
set cmd_file   = ""              # def: same name as viewer
set qc_prefix  = ""              # def: autoname; user can enter
set postfix    = "_"             # stick into name

# ------------------- process options, a la rr ----------------------

if ( $#argv == 0 ) goto SHOW_HELP

set ac = 1
while ( $ac <= $#argv )
    # terminal options
    if ( ("$argv[$ac]" == "-h" ) || ("$argv[$ac]" == "-help" )) then
        goto SHOW_HELP
    endif
    if ( "$argv[$ac]" == "-ver" ) then
        goto SHOW_VERSION
    endif

    # -------------- input opts --------------------
    # here, specify NIFTI *files*, not directories
    if ( "$argv[$ac]" == "-in_dwi" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set idwi = "$argv[$ac]"

    else if ( "$argv[$ac]" == "-select" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set KEEP = "$argv[$ac]"

    # [PT: Jan 09, 2018] well, just take in a file directly
    else if ( "$argv[$ac]" == "-select_file" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set KEEPfile = "$argv[$ac]"

    # ------------- input vecmat and bval --------------
    else if ( "$argv[$ac]" == "-in_col_matA" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        set invecmat[1]  = $argv[$ac] 
        @ ac += 1
        set invecmat[2]  = "$argv[$ac]"
        # output postfix; [1] and [3] sorted out, below
        set outvecmat[2] = "_matA.dat"

    else if ( "$argv[$ac]" == "-in_col_matT" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        set invecmat[1]  = $argv[$ac] 
        @ ac += 1
        set invecmat[2]  = "$argv[$ac]"
        # output postfix; [1] and [3] sorted out, below
        set outvecmat[2] = "_matT.dat"

    else if ( "$argv[$ac]" == "-in_col_vec" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        set invecmat[1]  = $argv[$ac] 
        @ ac += 1
        set invecmat[2]  = "$argv[$ac]"
        # output postfix; [1] and [3] sorted out, below
        set outvecmat[2] = "_cvec.dat"

    else if ( "$argv[$ac]" == "-in_row_vec" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        set invecmat[1]  = $argv[$ac] 
        @ ac += 1
        set invecmat[2]  = "$argv[$ac]"
        # output postfix; [1] and [3] sorted out, below
        set outvecmat[2] = "_rvec.dat"
        set ll2       = "["
        set rr2       = "]"

    # not necessary; default is just empty
    else if ( "$argv[$ac]" == "-in_bvals" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        set invecmat[3]  = $argv[$ac] 
        @ ac += 1
        set invecmat[4]  = "$argv[$ac]"
        # output postfix; [1] and [3] sorted out, below
        set outvecmat[4] = "_bval.dat"
        # ugh, have to see if this is row or col
        set aa = `1d_tool.py -show_rows_cols -infile $invecmat[4]`
        if ( ${aa[3]:gas/,//} < ${aa[6]:gas/,//} ) then
            # -> is rows
            set ll4 = "["
            set rr4 = "]"
            set outvecmat[3] = "-out_row_bval_sep"
        else
            # -> is cols
            set ll4 = "{"
            set rr4 = "}"
            set outvecmat[3] = "-out_col_bval_sep"
        endif

    else if ( "$argv[$ac]" == "-unit_mag_out" ) then
        set do_unit_mag = "-unit_mag_out"

    # -------------- output opts --------------------
    else if ( "$argv[$ac]" == "-prefix" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set opref = "$argv[$ac]"

    else if ( "$argv[$ac]" == "-do_movie" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set movie = "$argv[$ac]"
        if ( ( $movie == "MPEG" ) || ( $movie == "AGIF" ) ) then
            echo "++ OK, will make a movie of type $movie."
        else
            echo "** ERROR: '$movie' is NOT an allowed movie format!"
            echo "      -> must be either 'MPEG' or 'AGIF'"
            goto BAD_EXIT
        endif
        # replace with both here for calling @dj*
        set movie = "-do_movie $movie"   

    # -------------- qc stuff ----------------

    else if ( "$argv[$ac]" == "-qc_prefix" ) then
        if ( $ac >= $#argv ) goto FAIL_MISSING_ARG
        @ ac += 1
        set qc_prefix = "$argv[$ac]"

    else if ( "$argv[$ac]" == "-no_qc_view" ) then
        set DO_VIEWER = "0"

    else if ( "$argv[$ac]" == "-no_cmd_out" ) then
        set output_cmd = 0

    else
        echo "** unexpected option #$ac = '$argv[$ac]'"
        goto BAD_EXIT
    endif

    @ ac += 1
end

# =======================================================================
# ============================ ** SETUP ** ==============================
# =======================================================================

# ============================ input files ==============================

echo "++ Start script version: $version"

# NEED these two inputs
if ( "$idwi" == "" ) then
    echo "** ERROR: no DWI file input?!"
    goto BAD_EXIT
else if ( "$invecmat[2]" == "" ) then
    echo "** ERROR: no gradient/matrix file input?!"
    goto BAD_EXIT
endif

# make sure we can read DWI OK
set check = `3dinfo "$idwi"`
if ( "$#check" == "0" ) then
    echo "** ERROR: can't find inset file:  $idwi !"
    goto BAD_EXIT
else
    echo "++ Found inset DWI file:   $idwi"
endif

# check for vec/mat file ???????????????/
if ( 0 ) then
if ( -f "$invecmat[2]" ) then
    echo "++ Found input vec/mat file:  $invecmat[2]"
else
    echo "** ERROR: can't find entered vec/mat file $invecmat[2] !"
    goto BAD_EXIT
endif
endif

# -------------------------------

if ( ( "$KEEP" == "" ) && ( "$KEEPfile" == "" ) ) then
    echo "** ERROR: need SOME selection string!"
    echo "          Use either '-select ...' or '-select_file ...'"
    goto BAD_EXIT
else if ( ( "$KEEP" != "" ) && ( "$KEEPfile" != "" ) ) then
    echo "** ERROR: Too many selection types used!"
    echo "          Use *either* '-select ...' *or* '-select_file ...'"
    goto BAD_EXIT
endif

if ( "$KEEPfile" != "" ) then
    echo "++ Have the selection string from ${KEEPfile}:"
    set KEEP = `cat "$KEEPfile"`
    echo "   $KEEP"
else
    echo "++ Have the selection string from the command line:"
    echo "   $KEEP"
endif

# ========================= output/working dir ==========================

if ( "$opref" == "" ) then
    echo "** ERROR: need '-prefix ...' option provided!"
    echo "   See the helpfile for more information."
    goto BAD_EXIT
else
    set odir = `dirname $opref`
    set opref = `basename $opref`
    echo ""
    echo "++ Based on prefix, the output directory will be:"
    echo "     $odir"
    echo "++ Based on prefix, the output prefix will be:"
    echo "     $opref"
    echo ""
endif

# check output directory, use input one if nothing given

# default output dir, if nothing input.
if ( ! -e "$odir" ) then
    echo "+* Output directory didn't exist.  Trying to make '$odir' now."
    mkdir "$odir"
endif

if ( 0 ) then
# and put working directory as subdirectory.
set wdir = $odir/$wdir

# make the working directory
if ( ! -e $wdir ) then
    echo "++ Making working directory: $wdir"
    mkdir $wdir
else
    echo "+* WARNING: Somehow found a premade working directory (?):"
    echo "      $wdir"

    # don't clean preexisting directories-- could be user mistake.
    echo "   NB: will *not* clean it afterwards."
    set DO_CLEAN = "0"
endif
endif

# file names for lots of outputs
set ocmd   = "${opref}_cmd.txt"      # name for output command
set omata  = "${opref}_bmatA.dat"    # name for full afni bmatrix
set omatt  = "${opref}_bmatT.dat"    # name for full afni bmatrix
set obvec  = "${opref}_bvec.dat"     # name for full afni grads
set obval  = "${opref}_bval.dat"     # name for full afni bvals
set odwi   = "${opref}_dwi.nii.gz"   # name for dwis

# file *types* for output (grad/matrix stuff); [2,4] are filled above
set outvecmat[1] = ${invecmat[1]:gas/in/out/}
# [PT: Jul, 2017]: now comes from inp ---> 
#set outvecmat[3] = ${invecmat[3]:gas/in/out/} 
set outvecmat[2] = "$odir/${opref}$outvecmat[2]"

if ( "$outvecmat[4]" != "" ) then
    set outvecmat[4] = "$odir/${opref}$outvecmat[4]"
endif

if ( "$invecmat[4]" != "" ) then
    set KEEP3 = $KEEP
endif

# in case someone wants just unit-mag vec|mat, we don't want to lose
# the bvalue info!
if ( "$do_unit_mag" != "" ) then
    if ( "$outvecmat[3]" == "" ) then
        set outvecmat[3] = "-out_col_bval_sep"
        set outvecmat[4] = "$odir/${opref}_bval.dat"
    endif
endif

# =======================================================================
# =========================== ** PROCESS ** =============================
# =======================================================================

echo "\n-----> STARTING $this_prog ---->"

# ---------------------------- CMD ---------------------------------

echo "\n\nThis command:"
echo "$this_prog $argv\n\n"

if ( "$cmd_file" == "" ) then
    set cmd_file = "$odir/$ocmd"
endif

# copy original command:
# dump copy of command into workdir/..
if ( $output_cmd == 1 ) then
    echo "++ Echoing the command to: $cmd_file"

    set rec_afni_ver = `afni -ver`
    echo "### AFNI version:"  > $cmd_file
    echo "# $rec_afni_ver\n"            >> $cmd_file

    echo "### Executed from the directory location:"  >> $cmd_file
    echo "# $here\n"            >> $cmd_file
    echo "### The command was:" >> $cmd_file
    echo "# $this_prog $argv"   >> $cmd_file
    echo "\n"                   >> $cmd_file
endif

# ======================== convert dicoms ===============================

if ( 1 ) then

    # make grads ...
    1dDW_Grad_o_Mat++                                     \
        -overwrite                                        \
        -echo_edu                                         \
        "$invecmat[1]" "$invecmat[2]""$ll2""$KEEP""$rr2"  \
        $do_unit_mag                                      \
        "$outvecmat[1]" "$outvecmat[2]"                   \
        "$outvecmat[3]" "$outvecmat[4]"                   \
        "$invecmat[3]" "$invecmat[4]""$ll4""$KEEP3""$rr4"
    # NB: the above works somewhat cheatingly by having the empty
    # strings at the end of the function call.

    3dcalc   -echo_edu                                           \
        -a     $idwi"[${KEEP}]"                                  \
        -expr "(a)"                                              \
        -prefix $odir/${opref}.nii.gz                            \
        -overwrite

    # Feb,2017: take images!
    if ( "$DO_VIEWER" == "1" ) then
        echo "++ Make AP images."
        set img_pref = "${opref}"

        $my_viewer_4d  \
            -inset  $odir/${opref}.nii.gz      \
            -prefix "$odir/$img_pref" 

        if ( "$movie" != "" ) then
            $my_viewer_4d \
                -inset  $odir/${opref}.nii.gz  \
                -prefix "$odir/$img_pref"      \
                $movie
        endif
    endif
endif

goto GOOD_EXIT

# ========================================================================
# ========================================================================

SHOW_HELP:
cat << EOF
# -----------------------------------------------------------------------

    The purpose of this function is to help filter out user-found and
    user-defined bad volumes from DWI data sets.  

    If a bad volume is found, then it should be removed from the 4D
    dset, and it also has to be removed from the gradient list and
    the bvalue list.  In addition, if the user is processing DWI data
    that was acquired with two sets of phase encodings for EPI
    distortion correction, then one wants to remove the same volume
    *from both sets*.  This script is designed to help facilitate this
    process in a scriptable manner (the script still has to be run twice,
    but hopefully with easy enough syntax to avoid confusion/bugs).

    The user has to input 

        1) a 4D volumetric data sets of N DWIs (NAME.nii.gz),

        2) and accompanying bvalue/bmatrix/bvector values that they
           want to be parsed; this could be a unit-magn bvec file + a
           file of bvalues, or it could be a single file of scaled
           vector|matrix values.

    The output will be in similar format to what was input (i.e., the
    type of bvector|bmatrix files matching what was input), but with a
    different prefix name and/or directory, and everything filtered in
    a consistent manner *hopefully*.

    Check out the function "fat_proc_select_vols" for a nice, GUI way
    to select the bad DWIs you want to get rid of and to build a
    selector nicely (courtesy of J. Rajendra).

    REQUIRES: AFNI.

    Ver. $version (PA Taylor, ${rev_dat})

# -----------------------------------------------------------------------

  RUNNING: 

    $this_prog  \
        -in_dwi    DDD                     \
        -select   'SSS'                    \
        {-select_file SF}                  \
        -prefix   PPP                      \
        {-in_col_matA|-in_col_matT|        \
         -in_col_vec|-in_row_vec} FFF      \
        {-in_bvals BBB}                    \
        {-unit_mag_out}                    \
        {-qc_prefix  QCPREF}               \
        {-no_cmd_out}                      \
        {-no_qc_view}                      \
        {-do_movie AGIF|MPEG}

  where:
  -in_dwi  DDD      :name of a 4D file of DWIs (required).

  -in_col_matA |
  -in_col_matT |
  -in_col_vec  |
  -in_row_vec  FFF  :one of these options must be used to input 
                     a bvec/bmat file from the gradients. Required.
                     Same type of output file is returned.

  -in_bvals BBB     :if the bvec/bmat is a file of unit-magnitude values,
                     then the bvalues can be input, as well (optional).

  -select 'SSS'     :a string of indices and index ranges for
                     selecting which volumes/grads/bvals to *keep*.
                     This is done in a generic form of the typical
                     AFNI format, and index counting starts at 0 and
                     the 'last' brick could be specified as '\$'.  An
                     example for skipping the index-4 and index-6
                     volumes in a data set: 
                        '0..3,5,7..\$' 
                     This string gets applied to the volume, bval|bvec|bmat
                     files for an input set. Required.
                     NB: there are neither square nor curly brackets used
                     here!
                     NB2: Always use the single quotes around the
                     selector expression. 
       or
  -select_file SF   :where SF is a file name whose only contents are a nice 
                     string of indices and index ranges for selecting which
                     volumes/grads/bvals to *keep*.  Like, literally just
                        0..3,5,7..\$
                     sitting alone in a file-- no apostrophes needed/wanted.

  -prefix    PPP    :output prefix for all the volumes and text files.
                     Required.

  -unit_mag_out     :if one wants to prevent an input bvalue file being
                     applied to unit-magnitude gradients|vecs|matrices,
                     or if one just wants to ensure that the output grad
                     information is unit magnitude, use this option.  If
                     this is used with just a vec/matrix file input, then
                     a b-value file will also be output (so b-value info
                     wouldn't be lost at this moment).  Optional.

  -qc_prefix QCPREF :can set the prefix of the QC image files separately
                     (default is '$opref').
   -no_qc_view      :can turn off generating QC image files (why?)
   -no_cmd_out      :don't save the command line call of this program
                     and the location where it was run (otherwise, it is
                     saved by default in the ODIR/).

    -do_movie AGIF | MPEG
                    :one can use this option with either of the given
                     arguments to output a movie of the newly created
                     dset.  Only those arguments can be used at
                     present.

# -----------------------------------------------------------------------

  EXAMPLES:

    1) ... with selector via the command line (again, note the single
       apostrophes around the selector!):

        $this_prog  \
            -in_dwi       UNFILT_AP/AP.nii.gz       \
            -in_col_matT  UNFILT_AP/AP_bmatT.dat    \
            -select       '0..5,8,20..\$'           \
            -prefix       FILT_AP/AP 

    
    2) ... with selector via file contents (where there would *not* be
       apostrophes in the string sitting in the file):

        $this_prog  \
            -in_dwi       UNFILT_AP/AP.nii.gz           \
            -in_col_matT  UNFILT_AP/AP_bmatT.dat        \
            -select_file  UNFILT_AP/dwi_sel_goods.txt   \
            -prefix       FILT_AP/AP 

# -----------------------------------------------------------------------

EOF
    goto GOOD_EXIT

SHOW_VERSION:
   echo "version  $version (${rev_dat})"
   goto GOOD_EXIT

FAIL_MISSING_ARG:
    echo "** ERROR! Missing an argument after option flag: '$argv[$ac]'"
    goto BAD_EXIT

BAD_EXIT:
   exit 1

GOOD_EXIT:
   exit 0
