#!/bin/tcsh -f
# fscalc

set VERSION = '$Id: fscalc,v 1.2 2010/05/14 18:20:20 greve Exp $';

set outvol = ();
set inops = ();
set tmpdir = ();
set cleanup = 1;
set LF = ();
set RunIt = 1;
set OutDataType = ();

set inputargs = ($argv);
set PrintHelp = 0;
if($#argv == 0) goto usage_exit;
set n = `echo $argv | grep -e -help | wc -l` 
if($n != 0) then
  set PrintHelp = 1;
  goto usage_exit;
endif
set n = `echo $argv | grep -e -version | wc -l` 
if($n != 0) then
  echo $VERSION
  exit 0;
endif
goto parse_args;
parse_args_return:
goto check_params;
check_params_return:

set outdir = `dirname $outvol`;
mkdir -p $outdir
if($#tmpdir == 0) set tmpdir = $outdir/tmpdir.fscalc.$$
mkdir -p $tmpdir

if($#LF == 0) set LF = $tmpdir/fscalc.log
if($LF != /dev/null) rm -f $LF
echo "Log file for fscalc" >> $LF
date  | tee -a $LF
echo "" | tee -a $LF
echo "cd `pwd`"  | tee -a $LF
echo $0 $inputargs | tee -a $LF
echo "" | tee -a $LF
cat $FREESURFER_HOME/build-stamp.txt | tee -a $LF
uname -a  | tee -a $LF

# Set up the first input (can be file or constant)
set tmpvol = $tmpdir/tmp.mgh
set i1 = $inops[1]; shift inops;
if( -e $i1) then
  # It is a file, not a constant
  set cmd = (mri_convert $i1 $tmpvol)
else
  # Assume it is a constant, then synthesize
  # a volume with all values equal to that const
  set template = ();
  foreach inop ($inops)
    if(-e $inop) then
      set template = $inop
      break;
    endif
  end
  if($#template == 0) then
    echo "ERROR: no inputs are files (that exist)"
    exit 1;
  endif
  set cmd = (mri_volsynth --template $template --pdf const \
    --val-a $i1 --o $tmpvol)
endif
echo $cmd | tee -a $LF
if($RunIt) then
  $cmd | tee -a $LF
  if($status) exit 1;
endif

# Now loop through inputs and operations list
while($#inops)
  echo "----------------------" | tee -a $LF
  # Next must be an operation
  set op = $inops[1]; shift inops;

  # Remove any leading dash in the op name
  set c = `echo $op | cut -c1`
  if("$c" == "-") set op = `echo $op | cut -c 2-`

  # Alias sum to be the same as add
  if($op == sum) set op = add;

  set opok = 0; # This is to catch unrecognized ops
  # Binary operations
  if($op == mul || $op == div || $op == mod   || $op == add || \
     $op == sub || $op == sqd || $op == atan2 || $op == mag || \
     $op == eq  || $op == lt  || $op == lte   || $op == gt || \
     $op == gte || $op == upl || $op == lrl   || $op == and || \
     $op == or  || $op == andbw || $op == orbw || $op == masked ) then
    set opok = 1;
    if($#inops == 0) then
      echo "ERROR: $op requires two arguments" | tee -a $LF
      exit 1;
    endif
    set i2 = $inops[1]; shift inops;
    set cmd = (mris_calc -o $tmpvol $tmpvol $op $i2)
    echo $cmd | tee -a $LF
    if($RunIt) then
      $cmd | tee -a $LF
      if($status) exit 1;
    endif
  endif

  # Unary operations
  if($op == sqr || $op == sqrt || $op == abs || $op == sign ||\
     $op == norm) then
    set opok = 1;
    set cmd = (mris_calc -o $tmpvol $tmpvol $op)
    echo $cmd | tee -a $LF
    if($RunIt) then
      $cmd | tee -a $LF
      if($status) exit 1;
    endif
  endif

  # Unrecognized operations
  if($opok == 0) then
    echo "ERROR: operation $op not recognized"
    exit 1;
  endif

end
echo "----------------------" | tee -a $LF

if($#inops != 0) then
  echo "ERROR: too many inputs" | tee -a $LF
  exit 1;
endif

set cmd = (mri_convert $tmpvol $outvol)
if($#OutDataType) set cmd = ($cmd -odt $OutDataType --no_scale 1)
echo $cmd | tee -a $LF
if($RunIt) then
  $cmd | tee -a $LF
  if($status) exit 1;
endif

if($cleanup) rm -rf $tmpdir

exit 0

###############################################

############--------------##################
parse_args:
set cmdline = ($argv);
while( $#argv != 0 )

  set flag = $argv[1]; shift;
  
  switch($flag)

    case "-o":
    case "--o":
      if($#argv < 1) goto arg1err;
      set outvol = $argv[1]; shift;
      breaksw

    case "-odt":
    case "--odt":
      if($#argv < 1) goto arg1err;
      set OutDataType = $argv[1]; shift;
      breaksw

    case "--log":
      if($#argv < 1) goto arg1err;
      set LF = $argv[1]; shift;
      breaksw

    case "--nolog":
    case "--no-log":
      set LF = /dev/null
      breaksw

    case "--tmp":
    case "--tmpdir":
      if($#argv < 1) goto arg1err;
      set tmpdir = $argv[1]; shift;
      set cleanup = 0;
      breaksw

    case "--nocleanup":
      set cleanup = 0;
      breaksw

    case "--cleanup":
      set cleanup = 1;
      breaksw

    case "--no-run":
      set RunIt = 0;
      breaksw

    case "--debug":
      set verbose = 1;
      set echo = 1;
      breaksw

    default:
      set inops = ($inops $flag)
      breaksw
  endsw

end

goto parse_args_return;
############--------------##################

############--------------##################
check_params:

if($#outvol == 0) then
  echo "ERROR: must spec output"
  exit 1;
endif
if($#inops < 2) then
  echo "ERROR: must spec an input and an operation"
  exit 1;
endif

goto check_params_return;
############--------------##################

############--------------##################
arg1err:
  echo "ERROR: flag $flag requires one argument"
  exit 1
############--------------##################
arg2err:
  echo "ERROR: flag $flag requires two arguments"
  exit 1
############--------------##################

############--------------##################
usage_exit:
  echo ""
  echo "fscalc input1 op <input2 op ...> --o outvol"
  echo ""
  echo "Binary Ops: mul, div, mod, sub, sqd, atan2, mag, eq, "
  echo " lt, lte, gt, gte, upl, lrl, and, or, andbw, orbw, masked"
  echo ""
  echo "Unary Ops: sqr, sqrt, abs, sign, norm"
  echo ""
  echo "--odt type : output data type (uchar|short|int|float)."
  echo "--help"
  echo ""
  echo " --debug"
  echo " --tmpdir tmpdir"
  echo " --nocleanup "
  echo " --log logfile"
  echo ""

  if(! $PrintHelp) exit 1;
  echo $VERSION
  cat $0 | awk 'BEGIN{prt=0}{if(prt) print $0; if($1 == "BEGINHELP") prt = 1 }'
  mris_calc -u

exit 1;

#---- Everything below here is printed out as part of help -----#
BEGINHELP

Frontend for the mris_calc FreeSurfer program for performing
mathematical operations on volumes/surfaces of data. This program
allows users to specify multiple operations on a single command line
(mris_calc only allows one). The mris_calc help appears at the end
of this help.

This program handles many file formats including nifti, compressed
nifti, mgh, mgz, img, bshort, bfloat. Even DICOMs can be used as
input. Inputs can also be a constant (eg, mul 100). All inputs volumes
must have the same dimension.

All internal operations are performed in float. The ouput data type
can be specified with --odt (uchar|short|int|float).

Supported binary operations include:

mul, div, mod, sub, sqd, atan2, mag, eq, lt, lte, gt, gte, upl, lrl,
and, or, andbw, orbw, masked, sum (same as add)

Supported unary operations include:

sqr, sqrt, abs, sign, norm

See the mris_calc help at the end of this for more info on these
operations.

Note that operations can have a dash infront of them (ie, "-mul" is
the same as "mul")

EXAMPLES:

1. Compute percent difference between two volumes:

  fscalc a.nii sub b.mgh div a.nii mul 100 --o pct.diff.nii.gz

NOTES: different input file formats. "100" is a constant

2. Compute the square of the sum of two volumes:

  fscalc --o pct.diff.nii  a.nii add b.mgh sqr

NOTES: Output (--o) is at the beginning. It does not matter
where it appears on the command line

3. Add 100 to each voxel in a file

  fscalc a.nii add 100 --o a+100.nii
  fscalc 100 add a.nii --o a+100.nii

NOTES: constant can appear first.

