#!/bin/bash

# imagej.sh - a not so simple wrapper script used to run ImageJ

#	Copyright © 2008 Paolo Ariano,
#                 © 2009 Andreas Tille
#	Authors: Paolo Ariano (paolo dot ariano at unito dot it)
#                Andreas Tille <tille@debian.org>
#	Last modified date: 01 December 2009

# This is a not so simple wrapper script used to run ImageJ in Unix but 
# optimized for Debian GNU/Linux, this is a merge between my original script 
# and a more exhaustive one from Jon Jackson (jjackson at familyjackson dot net)

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public Licenseas published by
# the Free Software Foundation, either version 3 of the License, or (at 
# your option) any later version. See the file Documentation/LGPL3 in the
# original distribution for details. There is ABSOLUTELY NO warranty.
# This program is free software, but comes with no warrenty or guarantee
# send bug reports or feedback to me or to debian bug tracking system


# setup environment
set +u # don't give error for unset variables (matters for environment variables)
shopt -s extglob # allow extended pattern matching

##################### DEFINE JAVA_HOME  #####################

if [ -z "$JAVA_HOME" ] ; then
    # This does not work see #505315
    JAVA_HOME=$(/usr/sbin/update-java-alternatives -l | sort | tail -1 | cut -d' ' -f 3)
    # Reverted to old version - see #558222 (Andreas Tille)
    # JAVA_HOME=$(dirname $(dirname $(dirname $(readlink /etc/alternatives/java))))
fi

##################### CREATE THE RIGHT ENVIRONMENT #####################

# ImageJ path
ij_path=/usr/share/java

#ImageJ user path
ij_user_path=$HOME/.imagej

# report errors to this user
ijadmin='debian-med-packaging@lists.alioth.debian.org'

# Documentation URL
doc_url='http://imagej.nih.gov/ij/'

# temp folder
ij_tmp=$HOME/.imagej/tmp

# default behaviour when an ImageJ window is already open
newwindow='true'
#newwindow='false'

# macro argument conjoining character
separator=':'
# a ' ' may work provided no arguments would contain spaces
# use macro functions:  args=getArgument(); argArray=split(args, ':'); 
# to recover macro arguments

declare -i mem
declare -i default_mem=500
declare -i min_mem=16
declare -i max_mem
declare -i free_mem

# other variables 
dir=`pwd`
user=`whoami`
host=`hostname`
if [[ -z "$DISPLAY" ]] ; then
    echo 'Display variable not set'
    echo 'If ImageJ fails to load, try '
    echo '% setenv DISPLAY yourcomputer:0'
    echo 'if you use the "csh" or for "bash" try'
    echo '% export DISPLAY=yourcomputer:0'
    display='default' 
else
    display="$DISPLAY"
fi

declare -i port=0
declare -i verbosity=0
images=''
macrocmd=''
macroargs=''

# max memory allocation is 1800MB on 32bit java and 4000 on 64bit java
if [[ `uname` == 'SunOS' ]] ; then
    arch='-d64'
	java_path="${ij_path}/jre64/bin/java"	
	max_mem=`vmstat | awk 'NR == 3 {fmem=int($5 / 1024); if (fmem < 4000) {print fmem} else {print 4000}}'`
	free_mem="max_mem"
	mem=${free_mem}/2*3
	if (( $mem > $default_mem || $mem < $min_mem )) ; then mem=$default_mem ; fi
elif [[ `uname` == 'Linux' ]] ; then
	if [[ `uname -m` == 'x86_64' ]] ; then
    	arch='-d64'
    	java_path="${ij_path}/jre64/bin/java"
    	max_mem=`free | awk 'NR == 2 {fmem=int($2 / 1024); if (fmem < 4000) {print fmem} else {print 4000}}'`
		free_mem=`free | awk 'NR == 3 {fmem=int($4 / 1024); if (fmem < 4000) {print fmem} else {print 4000}}'`
		mem=${free_mem}/3*2
		if (( $mem > $default_mem || $mem < $min_mem )) ; then mem=$default_mem ; fi
	else
		arch='-d32'
    	java_path="${ij_path}/jre/bin/java"
    	max_mem=`free | awk 'NR == 2 {fmem=int($2 / 1024); if (fmem < 1800) {print fmem} else {print 1800}}'`
		free_mem=`free | awk 'NR == 3 {fmem=int($4 / 1024); if (fmem < 1800) {print fmem} else {print 1800}}'`
		mem=${free_mem}/3*2
		if (( $mem > $default_mem || $mem < $min_mem )) ; then mem=$default_mem ; fi	
	fi
fi

# create plugins,macro,tmp dirs 
mkdir -p $ij_user_path/plugins 
mkdir -p $ij_user_path/macros
mkdir -p $ij_user_path/luts

# create imagej socket-lock directory if non existant 
if [[ ! -d "$ij_tmp" ]] ; then
    mkdir -p "$ij_tmp"
    #chmod 777 "$ij_user_path/tmp"
fi

# Warning on syntax change
# for var in "$@" ; do
 	#if [[ "$var" == @(-batch|-eval|-macro|-mem|-new|-port|-run|-verbose) ]] ; then
#	if [ $var == @(-batch|-eval|-macro|-mem|-new|-port|-run|-verbose) ] ; then
#		echo "ImageJ command line options have changed!" 1>&2
# 		echo "$var is no longer a valid option, type 'imagej -h'" 1>&2
# 		echo "for full usage" 1>&2
# 		exit 1
# 	fi
# done

# makes symbolik links from shared plugins, macros and luts 

ls /usr/share/imagej/plugins | while read p ; do
  if [ ! -e "$ij_user_path/plugins/$p" ] ; then
    ln -s /usr/share/imagej/plugins/$p $ij_user_path/plugins/$p
  fi
done

ls /usr/share/imagej/macros | while read p; do
  if [ ! -e "$ij_user_path/macros/$p" ] ; then
      ln -s "/usr/share/imagej/macros/$p" "$ij_user_path/macros/$p"
  fi
done

ls /usr/share/imagej/luts | while read p ; do
    if [ ! -e "$ij_user_path/luts/$p" ] ; then
	ln -s /usr/share/imagej/luts/$p $ij_user_path/luts/$p
    fi
done



##################### USAGE DESCRIPTION #####################

function usage {
    echo 
    echo 'Image display and analysis program. Opens formats including:'
    echo 'UNC, Analyze, Dicom, NIFTI, Tiff, Jpeg, Gif, PNG ...'  
    echo
    echo 'imagej [options] image [ image2 ... image3 ]'
    echo '    -h        print help and more options'
    echo '    -o        open images in an open ImageJ panel'
    echo '    -p <N>    open images in ImageJ panel number <N>' 
    echo "    -x <MB>   set available memory (default=${mem} max=${max_mem})"     
    echo 
}

function fullusage {
    echo 
    echo 'Image display and analysis program. Opens formats including:'
    echo 'UNC, Analyze, Dicom, NIFTI, Tiff, Jpeg, Gif, PNG ...'  
    echo 
    echo 'imagej [options] image [ image2 ... image3 ] -> open images'
    echo
    echo 'basic options:'
    echo '  -h        print help and more options'
    echo '  -o        open images in existing ImageJ panel if one exists'
    echo '  -p <N>    open images in existing ImageJ panel number <N>' 
    echo "  -x <MB>   set available memory (default=${mem} max=${max_mem})"
    echo
    echo 'advanced options:'
    echo '  -c        enable plugin compilation within imagej'
    echo '  -d        use development version'
    echo '  -v        be verbose (vv or vvv increases verbosity)'  
    echo
    echo 'options for batch processing:'
    echo "  -e 'Macro Code'            execute macro code"
    echo "  -r 'Menu Command'          run menu command"
    echo "Quotation marks '' are required around commands including spaces"
    echo 'Commands can be sent to open ImageJ panels with the -p option'
    echo 
    echo 'options for macros:'
    echo 'imagej [-i image] [-b|-m] [arg1 ... argN] '
    echo '  -b macro    run macro without graphics window' 
    echo '  -m macro    run macro' 
    echo '"image" will be opened before macro is run'
    echo 'all following arguments are passed to macro'        
    echo 
    echo "Documentation - $doc_url "
	echo "Report problems with this software to $ijadmin"
    echo
}

function macroCmdError {
	fullusage 
	echo 'Only one command option (-b -e -m OR -r) may be specified' 1>&2
	exit 1
}

# The best way to install .jar libraries required by plugins is to copy them 
# to the imagej ij_path=/usr/share/java alternatively or add the .jar
# filepath to the modules line below. Paths are separated by a colon
# modules="-cp ${ij_path}/ij.jar:${ij_path}/plugins/jars/dcmie.jar"
modules="-cp ${ij_path}/ij.jar"
 
# enable plugins to be compiled in imagej
tools="$JAVA_HOME/lib/tools.jar"


#####################  ARGUMENTS PARSING #####################

while getopts b:ce:hi:m:op:r:vx: options
do
	case $options in
		b)	if [[ -n "$macrocmd" ]] ; then macroCmdError ; fi
			macrocmd="-batch ${OPTARG}"
			;;
		c)	modules="${modules}:${tools}"
			;;
		e)  if [[ -n "$macrocmd" ]] ; then macroCmdError ; fi
			macrocmd='-eval'
			macroargs="'${OPTARG}'"
			;;
		h)  fullusage
			exit 0
			;;
		i)  images="${images}'${OPTARG}' "
			;;
		m)	if [[ -n "$macrocmd" ]] ; then macroCmdError ; fi
			macrocmd="-macro ${OPTARG}"
			;;
		o)  newwindow='false'
			;;
		p)	newwindow='false'
			port="${OPTARG}"
        	if (( "$port" < 1 || "$port" > 99 )) ; then
            	echo "${OPTARG} is not a permissible value for port number (-p)" 1>&2
            	exit 1
        	fi
			;;
		r)	if [[ -n "$macrocmd" ]] ; then macroCmdError ; fi
			macrocmd='-run'
			macroargs="'${OPTARG}'"
			;;
		v)	verbosity=verbosity+1
			if (( $verbosity == 2 )) ; then set -x ; fi
			if (( $verbosity == 3 )) ; then set -v ; fi
			;;
		x)	mem="${OPTARG}"
			if (( $mem < $min_mem || $mem > $max_mem )) ; then
            	echo "${OPTARG} is not a permissible value for memory (-x)" 1>&2
            	echo "min=${min_mem}, max=${max_mem}" 1>&2
            	exit 1				
			fi
			;;
		\?) usage
			exit 1 
			;;
	esac
done
			
declare -i i=1
while (( i < $OPTIND )) ; do
	shift
	i=i+1
done

#if (( "$#" == 0 )) ; then
#	usage
#fi	

# -b and -m options only:
# remaining command line arguments are passed as macro arguments 
# separated by $separator
if [[ -n "$macrocmd" && -z "$macroargs" ]] ; then
	while (( "$#" > 0 )) ; do
        if [[ -z "$macroargs" ]] ; then 
            macroargs="${1}"
        else
            macroargs="${macroargs}${separator}${1}"
        fi
        shift 		
	done
	macroargs="'$macroargs'"
fi
	
# protect possible spaces in image filenames
if (( "$#" > 0 )) ; then
	while (( "$#" > 0 )) ; do
		images="${images}'${1}' "
		shift 
	done
fi

##################### USING PORT #####################

# Creates a temp file indicating a port is in use by imagej
pushd "$ij_tmp" > /dev/null
declare -i count=1
portopen='false'
lockFileCreated='false'
declare -a locklist=(`ls | grep '[0-9][0-9]-.*'`)

if (( $verbosity > 0 )) ; then echo -e "locklist: \n ${locklist[*]}" ; fi

# port specified by user
if (( $port > 0 )) ; then
    # look for a lock on the port specified
    for lockname in ${locklist[*]} ; do
        prefix=`printf '%02u' $port`
        if [[ "$lockname" == ${prefix}-${user}-${host}* ]] ; then
        	# found lock on the requested port, owned by user on current display
            portopen='true'
            if (( $verbosity > 0 )) ; then echo "Using socket lock: $lockname" ; fi
            count=$port
            break
        elif ("$lockname" =~ ${prefix}-* ) ; then
            echo "Port $port is in use by some other user or a different host" 1>&2
            if (( $verbosity > 0 )) ; then echo "Port lock: $lockname" ; fi
            exit 1
        fi
    done 
    # specified port not in use 
    count=$port

# If existing window is requested, look for listening port
elif [[ "$newwindow" == 'false' && ${#locklist} != 0 ]] ; then
    # look for a lock on the current display for this user
    for lockname in ${locklist[*]} ; do
        if [[ "$lockname" == [0-9][0-9]-${user}-${host}-${display} ]] ; then
            portopen='true'
            if (( $verbosity > 0 )) ; then echo "Found socket lock: $lockname" ; fi
            # if a matching user/display is found, use this one
            count="${lockname%-*-*-*}"
            #count=`echo $lockname | sed  -e 's/^\([0-9][0-9]\).*/\1/' -e 's/^0//'` # csh?
            break
        fi
    done
fi

# if a new port is to be used
if [[ "$portopen" == 'false' ]] ; then
    # new window requested or no matching port found
    # if port is not specified, look for first free port
    if (( "$port" == 0 )) ; then 
        if (( ${#locklist} == 0 )) ; then
            # no active locks - use first port
            count=1
        else
            # active locks - check each port number so see if it is in use
            # this is not synchronised!!
            count=0
            inuse='true'
            while [[ "$inuse" == 'true' ]] ; do
                count=count+1
                prefix=`printf '%02u' $count`
                inuse='false'
                for lockname in ${locklist[*]} ; do
                    if [[ "$lockname" == ${prefix}-* ]] ; then
                        inuse='true'
                    fi
                done
            done
        fi
    fi
    # creating a new port lock
    prefix=`printf '%02u' $count`
    lockname=${prefix}-${user}-${host}-${display}
	if (( $verbosity > 0 )) ; then echo -n "creating lock $lockname ... " ; fi
	touch $lockname
	trap '\rm -f ${ij_tmp}/$lockname >/dev/null ; exit 1' EXIT TERM 
	# Quitting ImageJ sends EXIT, as does a kill/kill -9 
	# CTRL+C in terminal sends INT + EXIT
	# System shutdown sends TERM (+EXIT??)
	
	if (( $verbosity > 0 )) ; then  echo 'done' ; fi

    lockFileCreated='true'
	echo 'Open other images in this ImageJ panel as follows:'
	echo "  imagej -p $count <image1> [<image2> ... <imageN>]"
    if (( $verbosity > 0 )) ; then echo "Socket lock: $lockname" ; fi
	echo
fi

# Report number of port locks - more than 50 may indicate error in this script
# No, please don't do this - it bloats our mailing list!
#if [[ $count -gt 50 && $port == 0 && "$ijadmin" != '' ]] ; then
#mail -s "ImageJ ports on $host" $ijadmin << EOF
#Port locks on $host reached $count
#EOF
#fi

##################### FINALLY RUN IMAGEJ #####################

popd > /dev/null

if [ -d /usr/share/imagej/jni/ ] ; then
    jni=-Djava.library.path=$(cat /usr/share/imagej/jni/* | tr '\n' ':')
fi

if [ "$JAVA_HOME" ] ; then
	if (( $verbosity > 0 )) ; then
		echo ${modules}
    		echo $JAVA_HOME/bin/java ${arch} -mx${mem}m ${jni} ${modules} ij.ImageJ -ijpath ${ij_user_path} -port${count} ${images} ${macrocmd} ${macroargs}
	else
	eval $JAVA_HOME/bin/java ${arch} -mx${mem}m ${jni} ${modules} ij.ImageJ -ijpath ${ij_user_path} -port${count} ${images} ${macrocmd} ${macroargs}
	fi
else
  echo "No JVM found to run ImageJ"
  echo "Please apt-get install a JVM to run ImageJ or "
  echo "set JAVA_HOME if it's not a JVM from a Debian Package."
  exit 1
fi

exit 0

