#!/bin/tcsh

# initializations
set maskset = ""
set surfset = ""
set thickdir = "in2out_thickdir"
set increment = ""
set maxthick = ""
set resample = ""
set surfsmooth = "6"
set depthsearch = "3"
set inval = ""
set outval = ""
set maskval = ""
set keep_temp_files = ""
set heat_method = "HEAT_07"
set fs_dir = ""

# help
if ($# < 1) then
HELP:
    echo "@measure_in2out - compute thickness of mask using in2out method"
    echo "usage:"
    echo "@measure_in2out -maskset maskset -surfset surfacedset.gii -outdir thickdir"
    echo
    echo "where maskset is the dataset to find thickness"
    echo " with value of 1 for the mask value to find the thickness"
    echo " values of -1 and -2 for the inner and outer boundary values"
    echo " (inside and outside masks are treated equivalently)"
    echo "surfset is a surface to use to find normals into the volume"
    echo "output is in directory thickdir. If not specified, in2out_thickdir is used"
    echo
    echo 'This script finds thickness by finding the shortest distance to "inside"'
    echo 'and "outside" voxels for every voxel in a mask. The distance to the "inside"'
    echo 'and the distance to the "outside" are added together to be "thickness".'

    echo "For example, cortical/gray matter thickness can be found using a mask dataset"
    echo "with white matter defined as an inside value and all other voxels"
    echo "assigned to be outside voxels."
    echo
    echo "Because of limitations in the growth of the spheres used in this method,"
    echo "it is recommended to use oversampled data, particularly when using 1mm data"
    echo "See -resample option below"
    echo
    echo "The maskset must contain three distinct non-zero values"
    echo " the highest value is assumed the mask value, the lowest value is"
    echo " the outside value, and the inside value is that value+1."
    echo ' One example use might be "GM=1,WM=-1,Outside=-2"'
    echo
    echo "Main options:"
    echo "  -maskset mydset      mask dataset for input"
    echo "  -surfset mydset.gii  surface dataset onto which to map thickness"
    echo "                       (probably a pial/gray matter surface)"
    echo "  -outdir thickdir     output directory"
    echo
    echo "Other options:"
    echo
    echo "  -resample mm   resample input to mm in millimeters (put a number here)"
    echo '                 set this to half a voxel or \"auto\".'
    echo "                 No resampling is done by default"
    echo "                 Resampling is highly recommended for most 1mm data"
    echo "  -increment mm  test thickness at increments of sub-voxel distance"
    echo "                 default is 1/4 voxel minimum distance (in-plane)"
    echo "  -surfsmooth mm smooth surface map of thickness by mm millimeters"
    echo "                 default is 6 mm"
    echo "  -maxthick mm   search for maximum thickness value of mm millimeters"
    echo "                 default is 6 mm"
    echo "  -depthsearch mm map to surface by looking for max along mm millimeter"
    echo "                 normal vectors. default is 3 mm"
    echo "  -maskinoutvals v1 v2 v3  use v1 for value of mask, v2 and v3 for inside"
    echo '                 and outside mask values, e.g. "1 -2 -1"'
    echo "  -keep_temp_files do not delete the intermediate files (for testing)"
    echo "  -surfsmooth_method heattype heat method used for smoothing surfaces"
    echo "                 default is HEAT_07 but HEAT_05 is also useful for some models"
    echo "  -fs_cort_dir dirname use FreeSurfer SUMA directory from @SUMA_Make_Spec_FS"
    echo "                 for processing"
    echo
    echo "Output:"
    echo "   inout_dist.nii.gz - volumetric thickness/distance from in to out"
    echo "   in_and_out.nii.gz - volumetric distance to inside and outside in 2 volumes"
    echo "   inout_thick.niml.dset - unsmoothed thickness mapped to surface nodes"
    echo "   inout_thick_smooth.niml.dset - smoothed thickness mapped to surface nodes"
    echo
    echo "   Other datasets included in output:"
    echo "   maskset.nii.gz, maskset_rs.nii.gz - mask and optional resampled mask"
    echo "   anat.gii - surface representation of mask volume"
    echo "   quick.spec - simple specification file for surface to use with suma commands"
    echo
    echo "See related scripts and programs for computing thickness:"
    echo "    @measure_bb_thick, @measure_erosion_thick and SurfMeasures"
    exit
endif
echo here
# process user options
set ac = 1
while ( $ac <= $#argv )
    # maybe just a desperate plea for help
    if ( ( "$argv[$ac]" == "-help" ) || ( "$argv[$ac]" == "-HELP" )  || \
       ( "$argv[$ac]" == "--help" ) || ( "$argv[$ac]" == "-h" ) ) then
       goto HELP
    # get the basics - input dataset, surface and output directory
    # only the input is really required, but the other two are nice to have
    else if ( "$argv[$ac]" == "-maskset" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-maskset'"
            exit 1
        endif
        set  maskset = $argv[$ac]
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-surfset" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-surfset'"
            exit 1
        endif
        set  surfset = $argv[$ac]
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-outdir" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-outdir'"
            exit 1
        endif
        set  thickdir = $argv[$ac]
        @ ac ++; continue;
        
    # now tweak the options    
    else if ( "$argv[$ac]" == "-resample" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-resample'"
            exit 1
        endif
        set resample = $argv[$ac]
        if ("$resample" == "auto") then
           echo "resampling set to auto"
        else
           set resample = `ccalc -expr "$resample"`
           if ("$status" != "0") then
              echo "resample set to $argv[$ac] is not valid"
              exit 1
           endif 
           echo "resample voxel size to $resample mm"       
        endif
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-increment" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-increment'"
            exit 1
        endif
        set  increment = $argv[$ac]
	    set increment = `ccalc -expr "$increment"`
	    if ("$status" != "0") then
	  	   echo "increment set to $argv[$ac] is not valid"
		   exit 1
	    endif        
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-maxthick" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-maxthick'"
            exit 1
        endif
        set  maxthick = $argv[$ac]
	    set maxthick = `ccalc -expr "$maxthick"`
	    if ("$status" != "0") then
	  	   echo "maxthick set to $argv[$ac] is not valid"
		   exit 1
	    endif
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-surfsmooth" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-surfsmooth'"
            exit 1
        endif
        set surfsmooth = $argv[$ac]
	    set surfsmooth = `ccalc -expr "$surfsmooth"`
	    if ("$status" != "0") then
	  	   echo "surfsmooth set to $argv[$ac] is not valid"
		   exit 1
	    endif
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-surfsmooth_method" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-surfsmooth'"
            exit 1
        endif
        set heat_method = $argv[$ac]
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-depthsearch" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-depthsearch'"
            exit 1
        endif
        set depthsearch = $argv[$ac]
	    set depthsearch = `ccalc -expr "$depthsearch"`
	    if ("$status" != "0") then
	  	   echo "depthsearch set to $argv[$ac] is not valid"
		   exit 1
	    endif
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-maskinoutvals" ) then
        # set maskval for actual mask
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-maskinoutvals'"
            exit 1
        endif
        set maskval = $argv[$ac]
	    set maskval = `ccalc -expr "$maskval"`
	    if ("$status" != "0") then
	  	   echo "maskval set to $argv[$ac] is not valid"
		   exit 1
	    endif
        # now checkout the inside value
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-maskinoutvals'"
            exit 1
        endif
        set inval = $argv[$ac]
	    set inval = `ccalc -expr "$inval"`
	    if ("$status" != "0") then
	  	   echo "inval set to $argv[$ac] is not valid"
		   exit 1
	    endif

        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-maskinoutvals'"
            exit 1
        endif
        set outval = $argv[$ac]
	    set outval = `ccalc -expr "$outval"`
	    if ("$status" != "0") then
	  	   echo "outval set to $argv[$ac] is not valid"
		   exit 1
	    endif
	    
        @ ac ++; continue;
    else if ( "$argv[$ac]" == "-keep_temp_files" ) then
        set keep_temp_files = "keep"
        @ ac ++; continue;

    else if ( "$argv[$ac]" == "-ignore_unknown_options" ) then
        set ignore_unknown_options = "yes"
        @ ac ++; continue;

    else if ( "$argv[$ac]" == "-fs_cortical_dir" ) then
        @ ac ++
        if ( $ac > $#argv ) then
            echo "** missing parameter for option '-fs_cortical_dir'"
            exit 1
        endif
        set fs_dir = $argv[$ac]
        @ ac ++; continue;

    # unknown options - exit
    else 
        if ("$ignore_unknown_options" != "yes") then
          echo "** unknown option $argv[$ac]"
          exit 1
        endif
    endif
    @ ac ++
end

if (("$maskset" == "") && ("$fs_dir" == "")) then
   echo 'No input dataset. Please provide "-maskset mydset.nii"'
   echo '   or -fs_cort_dir suma_dir'
   exit 1
endif

set nvox = `3dBrickStat -count -non-zero $maskset`
if ("$status" != "0") then
   echo "Could not read $maskset"
   exit 1
endif

if ("$nvox" == "0" ) then
   echo "Dataset has no mask in it"
   exit 1
endif

if ("$surfset" != "") then
	if ( ! -e $surfset) then
		echo "Surface dataset $surfset does not exist"
		exit 1
	endif 
endif

# do everything in a new directory for simplicity
mkdir $thickdir

if (! -d $thickdir ) then
   echo "Could not create output directory. Check permissions."
   exit 1
endif

# for FreeSurfer cortical thickness, can get specific names to make in-out masks
#  and surface
if ("$fs_dir" != "") then
	if (( ! -e $fs_dir/std.141.lh.pial.gii) || \
        (! -e $fs_dir/aparc.a2009s+aseg_REN_gm.nii.gz) || \
        (! -e $fs_dir/aparc+aseg.nii))   then
		echo "Surface dataset $surfset does not exist"
		exit 1
	endif
    set surfset = $fs_dir/std.141.lh.pial.gii
    3dcalc -a $fs_dir/aparc.a2009s+aseg_REN_gm.nii.gz -datum byte \
       -expr 'step(a)' -prefix $thickdir/gm.nii.gz -overwrite
    3dcalc -a $fs_dir/aparc+aseg.nii -datum short \
       -expr 'amongst(a,2,7,41,46,251,252,253,254,255)' -prefix $thickdir/wm.nii.gz \
       -overwrite
    3dcalc -a $thickdir/gm.nii.gz -b $thickdir/wm.nii.gz \
       -expr 'step(a)-step(b)*not(a)-2*not(a)*not(b)' \
       -prefix $thickdir/maskset.nii.gz -datum short -overwrite
    set maskset = maskset.nii.gz
endif

# check if useful values of masks in dataset
if ("$inval" == "" ) then
   set vrange = `3dBrickStat -non-zero -min -max $maskset`

   # need mask defined for inside, outside and mask itself
   if ($vrange[1] != $vrange[2]) then
      set outval = $vrange[1]
      set inval = `ccalc -expr "$outval+1"`
      set maskval = $vrange[2]
   else
      echo "The in-out method needs a 3 value dataset to calculate thickness"
      exit 1
   endif
endif


# put original dataset into output directory with specific name 
#  this is easier and allows for mask range selectors, sub-brick selectors, and NIFTI
if("$fs_dir" == "") then
   3dTcat -prefix $thickdir/maskset.nii.gz -overwrite $maskset
endif

# simplifying naming output to just maskset something
set base = "maskset"
set maskset = maskset.nii.gz

if ($surfset != "") then
    ConvertSurface -i $surfset -o $thickdir/anat.gii -overwrite
endif

cd $thickdir
rm in*.nii.gz out*.nii.gz dist*.nii.gz

# loop distance to inside and distance to outside

# get voxel size - minimum of 2D square at least in-plane (should use cubic voxels though)
# but this will work for 2D images too (maybe)
set v3 = `3dinfo -d3 $maskset`
set voxsize = `ccalc "min(abs($v3[1]),abs($v3[2]))"`

# check if resampling is desired
if ("$resample" != "") then
   # for the "auto" way, use 1/2 voxel size
   if ("$resample" == "auto") then
      set voxsize = `ccalc -expr "${voxsize}/2"`
   else
      set voxsize = $resample         
   endif
   3dresample -rmode NN -prefix maskset_rs.nii.gz -overwrite -dxyz $voxsize $voxsize $voxsize -input $maskset
   set maskset = maskset_rs.nii.gz
endif

# starting radius in mm
set inirad = $voxsize

# maximum radius in mm (must be set for this method)
if ("$maxthick" == "") then
   set maxrad = 3
else
   set maxrad = `ccalc -expr "$maxthick/2+0.01"`
endif

# increment radial search in mm
#  if nothing set, use 1/4 voxels
if ("$increment" == "") then
   set radinc = $voxsize

   set r1 = `ccalc "0.25*$voxsize"`
   set r2 = `ccalc "0.5*$voxsize"`
   set r3 = `ccalc "0.75*$voxsize"`
   #set radoffs = ( 0.00 $edge_r $corner_r )
   set radoffs = ( 0.00 $r1 $r2 $r3 )
else
   set radinc = $increment
   set radoffs = ( 0.00 )
endif

# make a 1 voxel dilated version of the mask to catch only the necessary inside and outside voxels
3dmask_tool -inputs $maskset"<$maskval>" -dilate_inputs 1 -prefix masksetd1.nii.gz -overwrite
set rad = $inirad
set radi = 0
set donext = 1

# put spheres down at different sizes to find distance to inside and outside
while ( $donext == "1" )
 foreach radoff ($radoffs)
	set rad = `ccalc -expr "$inirad + $radinc * $radi + $radoff"`
	set donext = `ccalc -int "step($maxrad -$rad)"`
	if ($donext != "0") then
	   set rthick = `ccalc "max($voxsize,$rad-$voxsize/2)"`
	   # this command is the key step for this method **************************
	   #  for every voxel, find minimum spheres that reach inside and outside masks
	   3dLocalstat -nbhd "SPHERE($rad)" -stat has_mask -stat has_mask2 -mask masksetd1.nii.gz \
				   -maskvalue $inval -maskvalue2 $outval -unfillvalue $rthick \
				   -prefix dist_in_out$rad.nii.gz -overwrite $maskset         
	endif
 end
 @ radi ++
end

# find the minimum distance to the inside and the minimum distance to the outside
3dMean -prefix in_and_out_p.nii.gz -overwrite -min -non_zero dist_in_out*.nii.gz
# mask to just the target mask
3dcalc -a in_and_out_p.nii.gz -b $maskset"<$maskval>" -expr 'step(b)*a' -prefix in_and_out.nii.gz -overwrite

# add the minimum distance to inside and outside together to get a kind of thickness
# this should come out somewhat banded and consistent through the object (cortex)
3dTstat -sum -prefix inout_dist.nii.gz -overwrite in_and_out.nii.gz

# do some surface stuff if we have a surface to work with
#  could make one with IsoSurface, but if it's already done, we can use that instead
if($surfset != "") then
   quickspec -tn gii anat.gii -spec quick.spec
   # look inside to find the maximum of the minimum distances
   # because the distance should be somewhat consistent through
   # it should be less sensitive to the choice of how deep
   3dVol2Surf -spec quick.spec -surf_A anat.gii -sv $maskset \
     -grid_parent inout_dist.nii.gz -gp_index 0 -map_func max \
     -f_steps 10 -f_index nodes \
     -cmask "-a $maskset<$maskval> -expr astep(a,0.000000)" \
     -oob_value $voxsize -oom_value $voxsize \
     -use_norms -norm_len -$depthsearch \
     -dnode -1 -out_niml inout_thick.niml.dset -overwrite

#   SurfSmooth -spec quick.spec -surf_A anat.gii -met HEAT_07  \
#     -input inout_max.niml.dset -fwhm 2 \
#     -output inout_max_smooth2mm.niml.dset -overwrite

   # smooth data on surface 
   # (HEAT_05 method is more robust from quick tests than HEAT_07 method
   #  recommended in help for SurfSmooth but HEAT_07 gives smoother
   #  results that look reasonable)
   SurfSmooth -spec quick.spec -surf_A anat.gii -met $heat_method  \
     -input inout_thick.niml.dset -fwhm $surfsmooth \
     -output inout_thick_smooth.niml.dset -overwrite
endif

# unless the user is a hoarder, trash the intermediate files
if ("$keep_temp_files" != "keep") then
   rm dist_in_out*.nii.gz *.smrec masksetd1.nii.gz in_and_out_p.nii.gz 
endif

echo "#\!/bin/tcsh" > @show_thick
echo "setenv SUMA_Sym_I_Range NO" >> @show_thick
echo "suma -i anat.gii -drive_com " '"-com surf_cont -load_dset inout_thick_smooth.niml.dset \' >> @show_thick
echo '     -com surf_cont -Dim 0.4 -com surf_cont -I_range 0 5 \' >> @show_thick
echo '     -com viewer_cont -key b" &' >> @show_thick
echo "To show surface with thickness map, use this command:"
echo "   cd $thickdir; tcsh @show_thick"
echo "To see volume datasets of thickness, view inout_dist.nii.gz in the afni GUI"
