#!/bin/bash
#
# @package      hubzero-invokeapp
# @file         invoke_app
# @copyright    Copyright (c) 2010-2020 The Regents of the University of California.
# @license      http://opensource.org/licenses/MIT MIT
#
# Copyright (c) 2010-2020 The Regents of the University of California.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# HUBzero is a registered trademark of The Regents of the University of California.

########################################################################
#
# recordExitStatus ()
#
# write exit status to stderr
#
########################################################################

recordExitStatus ()
{
    local exitStatus=$?

    msg="exitStatus = ${exitStatus}"
    echoerr ${msg}

    exit ${exitStatus}
}

trap recordExitStatus EXIT

# Programs invoke depends upon

pixelflip=/usr/bin/pixelflip
icewm=/usr/bin/icewm-hubzero
icewm_captive=/usr/bin/icewm-captive
ratpoison=/usr/bin/ratpoison-captive
openbox=/usr/bin/openbox
submit=/usr/bin/submit
toolparams=/usr/bin/toolparams

exec_vars=toolparams

source /etc/environ.sh

# Controls access to ionhelper execution. Allowed when -C = rappture
unset IONHELPER_ALLOWED

usage ()
{
cat << EOF
 invoke_app [Options]

 Options (detailed description follows):

     -A tool arguments
     -c execute command in background
     -C command to execute for starting the tool
     -d working directory
     -e environment variable
     -f No FULLSCREEN
     -S No submit
     -n nanowhim version
     -p add to path
     -r rappture version
     -t tool name
     -T tool root directory
     -u use envionment packages
     -w specify alternate window manager

 Detailed description of the options:

  -A  Pass the provided enquoted arguments onto the tool. Example usage:
      -A "-q blah1 -w blah2"
      The options -q and -w are not parsed by invoke, but are passed on to
      the tool.

  -c  Commands to run in the background before the tool launches.
      Example usage: -c "echo hi" -c "filexfer"

  -C  Command to execute for starting tool. Tool's command line arguments
      can be included in this option, or can be placed in the -A option.
      Example usage: -C /apps/rappture/bin/simsim
      Example usage: -C "/apps/rappture/bin/simsim -tool driver.xml -values random"
      Example usage: -C /apps/rappture/bin/simsim -A "-tool driver.xml -values random"

  -d  Change to this working directory. By default change to session directory.

  -e  Set an environment variable. Example usage:
      -e LD_LIBRARY_PATH=@tool/../\${VERSION}/lib:\${LD_LIBRARY_PATH}
      Within the value part of this option's argument, the text \${VERSION}
      is automatically substituted with the value of the variable
      TOOL_VERSION. Similarly, the text @tool is substituted with
      the value of TOOLDIR. By setting the environment variable,
      you are overwritting its previous value.

  -f  No full screen - disable FULLSCREEN environment variable,
      used by Rappture, to expand the window to the full available size of
      the screen.

  -n  Sets \${nanowhim_version} which dictates which version of nanowhim is used.
      If left blank the version will default to current

  -p  Prepend to the PATH environment variable. Example usage:
      -p @tool/../\${VERSION}/bin. Within the value part of this option's
      argument, the text \${VERSION} is automatically substituted with the
      value of the variable TOOL_VERSION. Similarly, the text @tool is
      substituted with the value of TOOLDIR. By setting this option
      the PATH environment variable is adjusted, but not overwritten.

  -r  Sets \${rappture_version} which dictates which version of rappture is used.
      If left blank the version will default to the special keyword 'system',
      which represents whichever version is pointed to by the default rappture
      environment in 'use'. A 'use -e -r rappture' will be performed to figure
      out where rappture is installed.
      If set to the special keyword 'none', searching for rappture executables
      (rappture, simsim, about) will be skipped and use of these executables
      will be disabled.
      This flag works well on hubs where multiple versions of rappture are
      installed. Users can specify their own version of Rappture to use by
      updating the PATH environment variable to include the directory where
      the 'rappture' executable is installed.
      Example usages:
      -r dev                      # find rappture in this subdirectory
      -r tag_1.3.5-4755-1811      # find rappture in this subdirectory
      -r none                     # disable searching for rappture

  -S  Disable submit client and run job locally. This flag takes no arguments
      and is used for debugging. It disables the use of submit client from
      the -C command that will be executed. The default behavior, when the
      flag is not given, is to run the command through the submit client
      unless the command is "rappture", "simsim", "getrappturexml", or
      "nanowhim", none of which are run through the submit client. Setting
      the flag on the command line will add your command to the list of
      commands that do no run with the submit client.

  -t  Sets \${toolname} which is used while setting up tool paths for TOOLDIR
      and TOOLXML. \${toolname} is the short name (or project name) of the
      tool. It is the same as the name used in the source code repository.
      With respect to the tool contribution process, it is the "toolname"
      in the path /apps/toolname/version/rappture/tool.xml. Setting this
      option will change the paths searched while trying to locate tool.xml
      and the bin directory.

  -T  Tool root directory. This is the directory holding a checked out version
      of the code from the source code repository. It typically has the src,
      bin, middleware, rappture, docs, data, and examples directories
      underneath it. With respect to the tool contribution process, it is the
      "/apps/toolname/version" in the path
      /apps/toolname/version/rappture/tool.xml. Setting this option will
      change the paths searched while trying to locate tool.xml and the bin
      directory. Typically when testing this option is used to specify
      where the tool directory is. In this case, its the present working
      directory:  -T $PWD

  -u  Set use scripts to invoke before running the tool. Example usage:
      -u octave-3.2.4 -u petsc-3.1-real-gnu
      These would setup octave-3.2.4 and petsc-3.1 in the environment that
      your tool would launch in.

  -w  Set the window manager. The default value is to use the ratpoison window
      manager if it exists. If ratpoison is not installed on the system, look
      for the icewm captive window manager setup. Use this flag to choose an
      alternative window manager.  If your application does not require a window
      manager specify headless.  The possible options are headless, ratpoison,
      captive, and icewm. If multiple options are specified the first one listed
      is selected.
EOF
}

########################################################################
#
# echoerr ()
#
# echo text to standard error
#
########################################################################

echoerr ()
{
    echo "$@" 1>&2;
}

########################################################################
#
# screenerr ()
#
# display error message on screen
#
########################################################################

screenerr ()
{
    if [[ -n ${wm} ]] ; then
        if [[ ${wm} != headless ]] ; then
            if [[ `which xmessage` != "" ]] ; then
                xmessage -center $1
            fi
        fi
    fi
}

########################################################################
#
# setup_base_dir (component)
#
# find the directory holding the different versions of "component".
# set this directory as the value of ${component}_base variable.
#
########################################################################

setup_base_dir ()
{
    local dist
    local os_version
    local dist_version
    local share_base
    local share_arch
    local arch
    local msg


    if [[ "$1" == "" ]]; then
        msg="while in setup_base_dir: missing component argument"
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi

    # determine Debian OS version, 5 - lenny, 6 - squeeze, 7 - wheezy
    dist=debian
    os_version=$(grep Linux /etc/issue | sed -e "s/.*Linux //" -e "s/\.[0-9]*//" -e "s/ .*//")
    dist_version=${dist}${os_version}

    # setup the name of the share directory where external software
    # should be installed.
    # share_base is for bit ambiguous software (probably scripts)
    # share_arch is for bit specific software (32-bit vs 64-bit compiled stuff)
    share_base="share"
    share_arch=""
    arch=$(uname -m)
    if [[ "${arch}" == "x86_64" ]] ; then
        # setup for 64-bit share envionments
        share_arch="share64"
    elif [[ "${arch}" == "x86" ]] ; then
        # setup for 32-bit share envionments
        share_arch="share32"
    elif [[ "${arch}" =~ i[3-6]86 ]] ; then
        # nanohub lenny containers match this
        # setup for 32-bit share envionments
        share_arch="share32"
    else
        echoerr "while in setup_base_dir for ${1}:"
        echoerr "unsupported architecture: ${arch}"
        echoerr "setting share_arch=\"\""
        echoerr "this could affect setting ${1}_base"
        share_arch=""
    fi

    if   [[ -d /apps/${share_arch}/${dist_version}/${1} ]] ; then
        eval ${1}_base="/apps/${share_arch}/${dist_version}/${1}"
    elif   [[ -d /apps/${share_arch}/${1} ]] ; then
        eval ${1}_base="/apps/${share_arch}/${1}"
    elif [[ -d /apps/${share_base}/${dist_version}/${1} ]] ; then
        eval ${1}_base="/apps/${share_base}/${dist_version}/${1}"
    elif [[ -d /apps/${share_base}/${1} ]] ; then
        eval ${1}_base="/apps/${share_base}/${1}"
    elif [[ -d /apps/${1} ]] ; then
        eval ${1}_base="/apps/${1}"
    else
        msg="while in setup_base_dir: could not set ${1}_base"
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi
}

########################################################################
#
# check_dependencies ()
#
# check that the programs invoke_app depends on are installed.
#
########################################################################

check_dependencies ()
{
    local p
    local execv

    for execv in ${exec_vars} ; do
        p=`eval echo '$'${execv}`
        if [[ ! -x "${p}" ]] ; then
            echoerr "cannot find the executable for ${execv}: ${p}"
            eval ${execv}=""
        fi
    done
}

########################################################################
#
# has_template_variables ()
#
# does the provided command have template variables in it?
# returns 0 for True, any thing else is False
#
########################################################################

has_template_variables()
{

    # template variables take on the form @@type(name)
    # where type is a string of alphabetical characters,
    # and name is a string of any characters other than
    # a closing paren.
    if [[ "$@" =~ @@[a-zA-Z]+\([^\)]+\) ]] ; then
        # command does have template variables
        return 0;
    else
        # command does not have template variables
        return 1;
    fi
}

########################################################################
#
# parse_tool_commands ()
#
# look through the -C options to see if we need to call toolparams
# construct the tool command and submit command we will use to launch
# the tool. add any extra tool arguments needed for rappture, simsim,
# and nanowhim calls.
#
# this procedure sets the ${toolcmd} and ${submit} variables
# it also changes the ${wm} variable if running nanowhim
#
########################################################################

parse_tool_commands()
{
    # check the number of -C commands provided
    toolcmd=""
    if  [ "${nToolCommands}" -eq "0" ] ; then
        # no -C options were provided,
        # default to running rappture
        toolcmds[$nToolCommands]="rappture"
        let nToolCommands++
        toolcmd="rappture"
    elif [ "${nToolCommands}" -eq "1" ] ; then
        # one -C option was provided
        # check for template variables
        if has_template_variables ${toolcmds[0]} ; then
            # there are template variables in the command
            # send command to toolparams for evaluation
            toolcmd="${toolparams} '${toolcmds[0]}'"
        else
            # no template variables
            # make this our toolcmd
            toolcmd=${toolcmds[0]}
        fi
    elif [ "${nToolCommands}" -gt "1" ] ; then
        # multiple -C flags were provided.
        # combine them into one string, so we can hand them
        # off to the toolparams program for parsing.
        local tptemplates=""
        local tpdefault=""
        local command
        for command in "${toolcmds[@]}" ; do
            echoerr "parsed toolparams command \"${command}\""
            # FIXME: look for "@@" template variables to tell
            #        if this is a template or a default.

            if has_template_variables ${command} ; then
                if [[ "${tptemplates}" == "" ]] ; then
                    tptemplates="'${command}'"
                else
                    tptemplates="${tptemplates} -or '${command}'"
                fi
            else
                tpdefault="-default '${command}'"
            fi
        done

        # build the tool command
        # if we were only given non-template commands,
        # don't use toolparams to lauch the command.
        # launch the last command and ignore all previous commands
        if [[ "${tptemplates}" == "" ]] ; then
            # no template commands, launch the last command
            toolcmd="${command}"
        else
            # has template commands, let toolparams launch the command
            toolcmd="${toolparams} ${tptemplates} ${tpdefault}"
        fi
    fi

    # settings for specific tool commands like rappture and simsim

    local nanowhim=""

    if   [[ "${toolcmd}" == "rappture" ]] ; then
        # rappture does not use submit
        submit=""
        setup_toolxml
        toolcmd="${RAPPTURE_PATH}/bin/rappture"
        toolargs="-tool ${TOOLXML} ${toolargs}"
        export IONHELPER_ALLOWED=1
    elif [[ "${toolcmd}" == "simsim" ]] ; then
        # simsim does not use submit
        submit=""
        toolcmd="${RAPPTURE_PATH}/bin/simsim"
    elif [[ "${toolcmd}" == "getrappturexml" ]] ; then
        # getrappturexml does not use submit
        submit=""
        setup_toolxml
        echo "RAPPTURE_XML="${TOOLXML}
        exit 0
    elif [[ "${toolcmd}" == "nanowhim" ]] ; then
        # nanowhim does not use submit
        submit=""
        setup_base_dir "nanowhim"
        nanowhim="${nanowhim_base}/${nanowhim_version}/bin/tclkit"
        setup_whimrc
        toolcmd="${nanowhim}"
        toolargs="${nanowhim_base}/${nanowhim_version}/src/nanoWhim.kit -file ${WHIMRC} ${toolargs}"
        # when nanowhim runs, we don't need a window manager
        wm=""
    else
        # otherwise, if using submit and it exists, do a local submit
        if [[ "${submit}" != "" ]] ; then
            submit="${submit} --local --noHeartbeat --metrics"
        else
            # not using submit
            echoerr "submit disabled: not using submit to launch this job."
        fi
    fi

    echoerr "toolcmd = ${toolcmd}"
    echoerr "toolargs = ${toolargs}"
    echoerr "submit = ${submit}"
}

########################################################################
#
# get_resource_info ()
#
# Sets variables resource_tool_title, resource_tool_version
#
# Extract tool information from the resources file if present.
#
# The resources file should be able to tell us the version of the
# running tool. When version is "test", this usually means the tool is in
# the contribtool state of "Installed" and was started from within
# contribtool. When version is a numeric value, this usually means the
# tool is in the contribtool state of "Published".
#
########################################################################

get_resource_info ()
{
    local version
    local caseMatch

    if [[ "$SESSIONDIR" != "" ]] ; then
        if [[ -r ${SESSIONDIR}/resources ]] ; then
            version=$(grep '^version' ${SESSIONDIR}/resources | cut -d' ' -f 2)
            resource_tool_title=$(grep '^application_name' ${SESSIONDIR}/resources | cut -d' ' -f 2-)
            caseMatch=$(shopt -p nocasematch)
            shopt -s nocasematch
            case $version in
                test )
                       resource_tool_version=dev
                       ;;
                   * )
                       resource_tool_version=$version
                       ;;
            esac
            $caseMatch
        fi
    fi
}


########################################################################
#
# setup_tooldir ()
#
# Set and export TOOLDIR variable.
#
# This function expects the variables ${toolname} and TOOL_VERSION to have
# been set prior to entry. This function painfully tries all sorts
# of combinations to correctly figure out where the tool.xml is
# located.
#
# Paths checked:
#
# /apps/${toolname}/${TOOL_VERSION}/rappture/tool.xml
# ${TOOLDIR}/rappture/tool.xml
# /apps/${toolname}/${TOOLDIR}/rappture/tool.xml
#
# TOOLDIR is the directory of the tool to be run, something like
# /apps/<toolname>/<version>
#
########################################################################

setup_tooldir ()
{
    local newtooldir=""
    local msg=""

    if [[ "${TOOLDIR}" == "" ]] ; then
        # Scenario #1
        #    -T flag was not provided and setup_installdir could not set TOOLDIR
        #    ${TOOLDIR} == ""
        #    continue looking for TOOLDIR based on ${toolname}

        if [[ "${toolname}" != "" ]] ; then
            if   [[ -h /apps/${toolname}/current ]] ; then
                newtooldir=$(readlink -f /apps/${toolname}/current)
            elif [[ -d /apps/${toolname}/current ]] ; then
                newtooldir=/apps/${toolname}/current
            fi
        fi

        # last guess to set a reasonable tooldir
        # is to use the present working directory
        if [[ "${newtooldir}" == "" ]] ; then
            newtooldir=$(pwd)
        fi

    else
        # Scenario #2
        #    -T flag was not provided, but setup_installdir found tooldir
        #    TOOLDIR="somedirectory"
        #    this is the trivial case because if setup_installdir set TOOLDIR,
        #    it should already exist.
        #
        # Scenario #3
        #    -T flag was provided
        #    TOOLDIR="somedirectory"
        #    this is the case where the user provided a TOOLDIR.
        #    check if directory exists, if not echo "bad directory"; exit 1

        if [[ -d ${TOOLDIR} ]] ; then
            newtooldir=$(readlink -f ${TOOLDIR})
        else
            if [[ "${toolname}" != "" ]] ; then
                if [[ -d /apps/${toolname}/${TOOLDIR} ]] ; then
                    # if the -t option is given, allow user to say
                    # -T r43
                    newtooldir="/apps/${toolname}/${TOOLDIR}"
                fi
            else
                if [[ -d /apps/${TOOLDIR} ]] ; then
                    # allow users to say
                    # -T toolname/r43
                    newtooldir="/apps/${TOOLDIR}"
                fi
            fi
        fi

        if [[ ! -d ${newtooldir} ]] ; then
            msg="while checking tool: ${TOOLDIR} is not a directory"
            echoerr ${msg}
            screenerr "${msg}"
            exit 1
        fi
    fi

    TOOLDIR=${newtooldir}

    export TOOLDIR
}

########################################################################
#
# setup_toolxml ()
#
# setup TOOLXML environment variable for rappture configuration
#
# TOOLXML is the full path to the tool.xml to be used while launching
# the program.
#
########################################################################

setup_toolxml ()
{
    local msg=""

    # for rappture tools, we look for tool.xml
    # use ${TOOLDIR} to find tool.xml
    if [[ -e ${TOOLDIR}/rappture/tool.xml ]] ; then
        TOOLXML="${TOOLDIR}/rappture/tool.xml"
    elif [[ -e  ${TOOLDIR}/tool.xml ]] ; then
        TOOLXML="${TOOLDIR}/tool.xml"
    else
        msg="Unable to find tool.xml file. It should be located at \"${TOOLDIR}/rappture/tool.xml\""
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi

    export TOOLXML
}

########################################################################
#
# setup_whimrc ()
#
# setup WHIMRC environment variable for nanowhim configuration
#
########################################################################

setup_whimrc ()
{
    local msg=""

    # for nanowhim tools, we look for nanowhimrc
    # use ${TOOLDIR} to find nanowhimrc
    if   [[ -e ${TOOLDIR}/middleware/nanowhimrc ]] ; then
        WHIMRC="${TOOLDIR}/middleware/nanowhimrc"
    else
        msg="Unable to find nanowhimrc file. It should be located at \"${TOOLDIR}/middleware/nanowhimrc\""
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi

    export WHIMRC
}

########################################################################
#
# find_application_revision ()
#
# setup SUBMIT_APPLICATION_REVISION environment variable for
# usage metrics.
#
########################################################################

find_application_revision ()
{
    local revisionPath=""
    local revisionDir=""
    local applicationPath=""
    local applicationDir=""
    local toolXMLDir=""
    local svnURL=""
    local toolrevision=""

    SUBMIT_APPLICATION_REVISION=""
    if   [ -n "${TOOLXML}" ] ; then
       TOOLXMLPATH=$(readlink -f ${TOOLXML})
       if   [ ${TOOLXMLPATH:0:5} = "/apps" -o ${TOOLXMLPATH:0:10} = "/auto/apps" ] ; then
          revisionPath=${TOOLXMLPATH%%/rappture*}
          revisionDir=$(basename ${revisionPath})
          applicationPath=$(dirname ${revisionPath})
          applicationDir=$(basename ${applicationPath})
          SUBMIT_APPLICATION_REVISION=${applicationDir}_${revisionDir}
          if [ ${TOOLXMLPATH} != "/apps/${applicationDir}/${revisionDir}/rappture/tool.xml" ] ; then
             toolXMLDir=$(dirname ${TOOLXMLPATH})
             revisionPath=$(basename ${toolXMLDir})
             revisionDir=$(basename ${revisionPath})
             SUBMIT_APPLICATION_REVISION="${SUBMIT_APPLICATION_REVISION}:${revisionDir}"
          fi
       elif [ ${TOOLXMLPATH:0:5} = "/home" -o ${TOOLXMLPATH:0:10} = "/auto/home" ] ; then
          svnURL=$(svn info ${TOOLXMLPATH} 2> /dev/null | grep 'URL:')
          if [ -n "${svnURL}" ] ; then
             applicationPath=${svnURL%%/svn*}
             applicationDir=${applicationPath##*/}
             SUBMIT_APPLICATION_REVISION=${applicationDir}_HOME
          else
             revisionPath=${TOOLXMLPATH%%/rappture*}
             if   [ ${revisionPath} != ${TOOLXMLPATH} ] ; then
                revisionDir=$(basename ${revisionPath})
                SUBMIT_APPLICATION_REVISION=${revisionDir}_DEV
             elif [ -n "${toolname}" -a -n "${TOOL_VERSION}" ] ; then
                SUBMIT_APPLICATION_REVISION=${toolname}_${TOOL_VERSION}
             fi
          fi
       fi
    elif [ -n "${TOOLDIR}" ] ; then
       TOOLDIRPATH=$(readlink -f ${TOOLDIR})
       if   [ ${TOOLDIRPATH:0:5} = "/apps" -o ${TOOLDIRPATH:0:10} = "/auto/apps" ] ; then
          toolrevision=${TOOLDIRPATH##*/apps/}
          applicationDir=$(dirname ${toolrevision})
          revisionPath=${toolrevision#*/}
          revisionDir=${revisionPath%%/*}
          SUBMIT_APPLICATION_REVISION=${applicationDir}_${revisionDir}
       elif [ ${TOOLDIRPATH:0:5} = "/home" -o ${TOOLDIRPATH:0:10} = "/auto/home" ] ; then
          svnURL=$(svn info ${TOOLDIRPATH} 2> /dev/null | grep 'URL:')
          if [ -n "${svnURL}" ] ; then
             applicationPath=${svnURL%%/svn*}
             applicationDir=${applicationPath##*/}
             SUBMIT_APPLICATION_REVISION=${applicationDir}_HOME
          else
             revisionDir=$(basename ${TOOLDIRPATH})
             SUBMIT_APPLICATION_REVISION=${revisionDir}_DEV
          fi
       fi
    fi
    export SUBMIT_APPLICATION_REVISION
}

########################################################################
#
# substitute_command_templates(template)
#
# substitute templates like '@tool' in command strings.
#
# converts:
# '@tool' -> ${TOOLDIR}
#
# returns a string without template placeholders.
#
########################################################################

substitute_command_templates ()
{

    local template=$1
    local result=""

    result=$(echo ${template} | sed -e "s;@tool;${TOOLDIR};g")

    echo ${result}
}


########################################################################
#
# substitute_env_variable_templates(template)
#
# substitute templates like '@tool' and '${VERSION}' in environment
# variable strings.
#
# converts:
# '@tool' -> ${TOOLDIR}
# '${VERSION}' -> ${TOOL_VERSION}
#
# returns a string without template placeholders.
#
########################################################################

substitute_env_variable_templates ()
{

    local template=$1
    local result=""

    result=$(echo ${template} |
                sed -e "s;@tool;${TOOLDIR};g" \
                    -e "s/\${VERSION}/${TOOL_VERSION}/")

    echo ${result}
}


########################################################################
#
# start_bg_commands ()
#
# run the setup and background commands prior to starting the rappture
# program. This function starts up the about pages and runs the
# background commands set in the -c option.
#
########################################################################

start_bg_commands ()
{
    export RAPPTURE_DEBUG=1
    export FILEXFER_DEBUG=1
    export RAPPTURE_POSTERN=lumous

    local cmd

    # check for -c commands
    if [[ "${commands[0]}" != "" ]] ; then
      for cmd in "${commands[@]}" ; do
        cmd=$(substitute_command_templates "${cmd}")
        echoerr "background exec'ing \"${cmd}\""
        eval ${cmd}&
      done
    fi
}

########################################################################
#
# select_window_manager ()
#
# select a window manager from supplied options.
# Lowest functionality wins.
#
########################################################################

select_window_manager ()
{
    local icewmOK=0
    local captiveOK=0
    local ratpoisonOK=0
    local headlessOK=0
    local openboxOK=0

    for checkWM in ${wm} ; do
        case ${checkWM} in
            openbox )
                if [[ `which openbox` == "" ]] ; then
                    msg="Unable to find the window manager \"openbox\""
                    echoerr ${msg}
                    screenerr "${msg}"
                else
                    if [[ ! -x ${openbox} ]] ; then
                        msg="Start script, \"${openbox}\" is not executable"
                        echoerr ${msg}
                        screenerr "${msg}"
                    else
                        openboxOK=1
                    fi
                fi
                ;;
            icewm )
                if [[ `which icewm` == "" ]] ; then
                    msg="Unable to find the window manager \"icewm\""
                    echoerr ${msg}
                    screenerr "${msg}"
                else
                    if [[ ! -x ${icewm} ]] ; then
                        msg="Start script, \"${icewm}\" is not executable"
                        echoerr ${msg}
                        screenerr "${msg}"
                    else
                        icewmOK=1
                    fi
                fi
                ;;
            captive )
                if [[ `which icewm` == "" ]] ; then
                    msg="Unable to find the window manager \"icewm\""
                    echoerr ${msg}
                    screenerr "${msg}"
                else
                    if [[ ! -x ${icewm_captive} ]] ; then
                        msg="Captive start script, \"${icewm_captive}\" is not executable"
                        echoerr ${msg}
                        screenerr "${msg}"
                    else
                        captiveOK=1
                    fi
                fi
                ;;
            ratpoison )
                if [[ `which ratpoison` == "" ]] ; then
                    msg="Unable to find the window manager \"ratpoison\""
                    echoerr ${msg}
                    screenerr "${msg}"
                else
                    if [[ ! -x ${ratpoison} ]] ; then
                        msg="Ratpoison start script, \"${ratpoison}\" is not executable"
                        echoerr ${msg}
                        screenerr "${msg}"
                    else
                        ratpoisonOK=1
                    fi
                fi
                ;;
            headless )
                headlessOK=1
                ;;
            none )
                msg="The none window manager has been deprecated. It has been replaced with the headless window manager"
                echoerr ${msg}
                screenerr "${msg}"
                headlessOK=1
                ;;
        esac
    done
    if   [[ ${headlessOK} -eq 1 ]] ; then
        wm="headless"
    elif [[ ${ratpoisonOK} -eq 1 ]] ; then
        wm="ratpoison"
    elif [[ ${captiveOK} -eq 1 ]] ; then
        wm="captive"
    elif [[ ${icewmOK} -eq 1 ]] ; then
        wm="icewm"
    elif [[ ${openboxOK} -eq 1 ]] ; then
        wm="openbox"
    else
        msg="Unable to select a window manager"
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi
    if [[ ${wm} != headless ]] ; then
        exec_vars="${exec_vars} pixelflip"
    fi
}

########################################################################
#
# start_window_manager ()
#
# start the pixelflip daemon to keep the vnc session active?
# start a window manager.
#
########################################################################

start_window_manager ()
{
    local pf
    local caseMatch
    local msg
    local wmcmd

    # Start the 5-minute keepalive daemon.
    for pf in ${pixelflip} ""; do
        if [[ -x ${pf} ]] ; then
            ${pf}
            break
        fi
    done
    if [[ "${pf}" == "" ]] ; then
        echoerr "No pixelflip executable found in: \"${pixelflip}\""
    fi

    caseMatch=$(shopt -p nocasematch)
    shopt -s nocasematch
    case ${wm} in
        openbox )
            wmcmd="${openbox} &"
            ;;
        icewm )
            wmcmd="${icewm} &"
            ;;
        captive )
            wmcmd="${icewm_captive} &"
            ;;
        ratpoison )
            wmcmd="${ratpoison} &"
            ;;
    esac
    ${caseMatch}

    # check for -f full screen
    if [[ "${fs}" == "" ]] ; then
        echoerr "setting FULLSCREEN=yes"
        export FULLSCREEN=yes
    else
        echoerr "disabling FULLSCREEN=yes"
    fi

    # Start the window manager.
    echoerr "setting up windowmanager: ${wm}"
    eval ${wmcmd}
}

########################################################################
#
# setup_workingdir ()
#
# Change to the provided working directory.
# Display error messages if there were troubles changing the directory.
#
########################################################################

setup_workingdir ()
{
    local msg=""

    if [ -d ${1} ] ; then
        cd ${1}
        if [ $? -ne 0 ] ; then
            msg="Could not change directory to \"${1}\""
            echoerr ${msg}
            screenerr "${msg}"
            exit 1
        fi
    else
        msg="Working directory \"${1}\" does not exist"
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi
}

########################################################################
#
# setup_path_envvar ()
#
# Add standard stuff to the PATH environment variable
# Currently we add the tool's bin dir
#
########################################################################

setup_path_envvar ()
{
    # add the tool's bin directory to the path
    if [[ -d ${TOOLDIR}/bin ]] ; then
       PATH=${TOOLDIR}/bin:${PATH}
    fi

    export PATH
}

########################################################################
#
# setup_installdir ()
#
# figure out where we are installed
# try to set TOOLDIR and toolname the best we can in case the user
# doesn't give us this information.
#
# sets the following variables:
#   toolname
#   TOOL_VERSION
#   TOOLDIR
#
########################################################################

setup_installdir ()
{
    local caller
    local dir
    local base

    toolname=""
    TOOL_VERSION=""
    TOOLDIR=""

    # this won't work on a mac because they have fake
    # versions of the readlink and realpath functions
    caller=$(ps -ocommand= -p $PPID | awk -F' ' '{print $2}' | awk '{print $1}')

    if [[ "${caller}" != "" ]] ; then
        caller=$(readlink -f ${caller})
        dir=$(dirname ${caller})
        base=$(basename ${caller})
        if [[ "${base}" == "invoke" ]] ; then
            # we were started by an invoke script
            # look for a familiar directory structure
            # to help with guessing toolname and TOOLDIR
            base=$(basename ${dir})
            if [[ "${base}" == "middleware" ]] ; then
                # caller's path probably looks something like one of these:
                # /apps/<toolname>/<version>/middleware/invoke
                # /apps/<toolname>/middleware/invoke
                # /home/<hub>/<user>/<toolname>/middleware/invoke
                # in the first case, <version> could look like:
                # "r1234" or "20081103"
                dir=$(dirname ${dir})
                base=$(basename ${dir})
                TOOLDIR=${dir}
                if     [[ "${base}" =~ (^r[0-9]+) ]] \
                    || [[ "${base}" =~ (^[0-9]{8}) ]] \
                    || [[ "${base}" == "current" ]] \
                    || [[ "${base}" == "dev" ]] ; then
                    # path probably looks something like this:
                    # /apps/<toolname>/<version>/middleware/invoke
                    TOOL_VERSION=${base}
                    toolname=$(basename $(dirname ${dir}))
                elif [[ "${base}" =~ [a-zA-Z0-9]+ ]] ; then
                    # path probably looks something like one of these:
                    # /apps/<toolname>/middleware/invoke
                    # /home/<hub>/<user>/<toolname>/middleware/invoke
                    TOOL_VERSION=""
                    toolname=${base}
                fi
            fi
        else
            # this covers a special case for aqme and 1dhetero on nanohub.org
            # where a user starts 1dhetero from a script inside of
            # aqme's bin directory.
            base=$(basename ${dir})
            if [[ "${base}" == "bin" ]] ; then
                # caller's path probably looks something like one of these:
                # /apps/<toolname>/<version>/bin/program
                # /home/<hub>/<user>/<toolname>/bin/program
                # in the first case, <version> could look like:
                # "r1234" or "20081103"
                dir=$(dirname ${dir})
                base=$(basename ${dir})
                if [[ "${base}" =~ (^r[0-9]+) ]] || \
                   [[ "${base}" =~ (^[0-9]{8}) ]] ; then
                    # path probably looks something like this:
                    # /apps/<toolname>/<version>/bin/program
                    TOOLDIR=${dir}
                    TOOL_VERSION=${base}
                    toolname=$(basename $(dirname ${dir}))
                fi
            fi
        fi
    # else
        # since the caller is blank, theres a good chance the user
        # is calling this script directly from the command line.
        # wait until we evaluate the command line arguments
        # and finish setting TOOLDIR in the function setup_tooldir
    fi

    echoerr "------------------------------------------------------"
    echoerr "guessed variable assignments before reading arguments:"
    echoerr "script = $(readlink -f ${0})"
    echoerr "caller = ${caller}"
    echoerr "toolname = ${toolname}"
    echoerr "TOOL_VERSION = ${TOOL_VERSION}"
    echoerr "TOOLDIR = ${TOOLDIR}"
    echoerr "------------------------------------------------------"
}


########################################################################
#
# check_rappture_tools (rapptureBaseDir)
#
# check if the provided directory has a bin directory with rappture tools
#
# returns 0 on success and 1 otherwise.
#
########################################################################

check_rappture_tools ()
{
    local rappture_base_dir=$1
    local result=0

    if [[ ! -x "${rappture_base_dir}/bin/rappture" ]] ; then
        # echoerr "invalid path for 'rappture': ${rappture_base_dir}/bin/rappture"
        result=1
    fi

    if [[ ! -x "${rappture_base_dir}/bin/simsim" ]] ; then
        # echoerr "invalid path for 'simsim': ${rappture_base_dir}/bin/simsim"
        result=1
    fi

    if [[ ! -x "${rappture_base_dir}/bin/about" ]] ; then
        # echoerr "invalid path for 'about': ${rappture_base_dir}/bin/about"
        result=1
    fi

    return ${result}
}


########################################################################
#
# setup_rappture_paths ()
#
# figure out where rappture is installed
#
# internally set RAPPTURE_PATH
# RAPPTURE_PATH points to the base of the rappture installation
# we assume there is a bin directory under RAPPTURE_PATH,
# where executables like 'rappture', 'simsim', and 'about' are stored.
#
########################################################################

setup_rappture_paths ()
{
    local rexe
    local t

    # check if we should look for rappture executables.
    if [[ "${rappture_version}" == "none" ]] ; then
        RAPPTURE_PATH=""
        echoerr "rappture_version set to none: disabling search for rappture executables.\""
        echoerr "rappture executables (rappture, simsim, about) will be disabled.\""
        return 0
    fi

    # try to find rappture
    #
    # check if rappture is already in the path

    rexe=`which rappture`

    # if we find /apps/bin/rappture, ignore it because it
    # is usually a shell script that eventually calls rappture.
    # we are looking for the rappture installation.
    if [[ "${rexe}" == "/apps/bin/rappture" ]] ; then
        rexe=""
    fi

    if [[ "${rexe}" == "" ]] ; then
        # rappture is not in the path
        # try to find an installation by enabling
        # the rappture environment through 'use'
        echoerr "grabbing RAPPTURE_PATH from \"use -e -r rappture\""
        RAPPTURE_PATH=$(. /etc/environ.sh; use -e -r rappture; echo $RAPPTURE_PATH)
    else
        # rappture is in the path,
        # set RAPPTURE_PATH based on where the executable lives
        RAPPTURE_PATH=$(dirname $(dirname ${rexe}))
    fi

    # check if we have a reasonable RAPPTURE_PATH
    if ! check_rappture_tools ${RAPPTURE_PATH} ; then
        msg="could not find a rappture installation: RAPPTURE_PATH=${RAPPTURE_PATH}, try adjusting the PATH environment variable with the -e flag"
        echoerr ${msg}
        screenerr "${msg}"
        exit 1
    fi

    # if the user provided a specific rappture version
    # by using the -r flag, try to honor it

    if [[ "${rappture_version}" != "system" ]] ; then
        t=$(dirname ${RAPPTURE_PATH})/${rappture_version}
        if ! check_rappture_tools ${t} ; then
            msg="failed to find rappture executables in specific rappture version: ${t}"
            echoerr ${msg}
            screenerr "${msg}"
            exit 1
        else
            echoerr "switching to rappture version: ${t}"
            RAPPTURE_PATH=${t}
        fi
    fi


    # paths for rappture tools are based off of RAPPTURE_PATH
    echoerr "RAPPTURE_PATH = ${RAPPTURE_PATH}"
}


# Start here

let nCommands=0
let nToolCommands=0
wm=""
fs=""
toolname=""
toolcmd=""
toolargs=""
goto_workingdir="false"
workingdirectory=""
resource_tool_title=""
resource_tool_version=""
rappture_version="system"
nanowhim_version="current"
use_submit="true"
options=":C:d:n:r:t:T:v:u:e:p:q:c:a:A:w:l:fsS"

# parse the command line flags and options
# separate flags from options

let nNamedArgs=0
while (( "$#" ))
do
   case $1 in
      -h )
           usage
           exit 0
           ;;
      -f|-s|-S )
           namedArgs[$nNamedArgs]=$1
           let nNamedArgs++
           shift
           ;;
      -* )
           namedArgs[$nNamedArgs]=$1
           let nNamedArgs++
           shift
           if [[ "$1" == "" || ${1:0:1} == "-" ]] ; then
               let nNamedArgs--
               echoerr "${namedArgs[$nNamedArgs]} requires an argument."
               exit 1
           else
               namedArgs[$nNamedArgs]=$1
               let nNamedArgs++
               shift
           fi
           ;;
       * )
           echoerr "The argument $1 will be ignored."
           shift
           ;;
   esac
done

get_resource_info

setup_installdir

while getopts "${options}" Option "${namedArgs[@]}"
do
   case $Option in
      C ) toolcmds[$nToolCommands]=$OPTARG;let nToolCommands++;;
      d ) workingdirectory=$OPTARG; goto_workingdir="true";;
      n ) nanowhim_version=$OPTARG;;
      r ) rappture_version=$OPTARG;;
      t ) toolname=$OPTARG;;
      T ) TOOLDIR=$OPTARG;;
      q ) echoerr "The -q option is no longer available and has no replacement option";exit 1;;
      c ) commands[$nCommands]=$OPTARG;let nCommands++;;
      a ) echoerr "The -a <html_file> option is no longer available and has been replaced with the option -c \"about <html_file>\"";exit 1;;
      A ) toolargs="$OPTARG";;
      w ) wm="${wm} $OPTARG";;
      l ) echoerr "The -l file1,file2 option is no longer available and has been replaced with the option -A \"-load file1,file2\"";exit 1;;
      f ) fs="no";;
      s ) echoerr "The -s option is no longer available and has been replaced with the option -C simsim";exit 1;;
      S ) use_submit="false";;
      e ) true;;
      p ) true;;
      u ) true;;
      : ) echoerr "Option -${OPTARG} requires an argument.";exit 1;;
      ? ) echoerr "Invalid option: -${OPTARG}";exit 1
   esac
done

if [[ ${wm} == "" ]] ; then
   wm="ratpoison"
fi

# find and export the TOOLDIR environment variable
setup_tooldir

#
# Finish parsing the arument list
# Two passes through argument list allows arbitrary order
# Reset argument pointer to the begining
#
OPTIND=1
while getopts "${options}" Option "${namedArgs[@]}"
do
   case $Option in
      e ) $(substitute_env_variable_templates "export $OPTARG");;
      p ) $(substitute_env_variable_templates "export PATH=$OPTARG:$PATH");;
      u ) use -e -r $OPTARG;;
   esac
done

# select window manager from options provided
select_window_manager

if [[ "${use_submit}" == "true" ]] ; then
   exec_vars="${exec_vars} submit"
else
   submit=""
fi

check_dependencies

# figure out where rappture and related tools are installed.
setup_rappture_paths

# look through the -C options and populate ${toolcmd}
parse_tool_commands

# find the application revision for submit metrics
find_application_revision

# Add standard stuff to the path,
# like the tool's bin directory
setup_path_envvar

#
# If there is no tty, this is not an interactive session.
# Start a window manager.  Make the app run full screen.
# Change dirs to the SESSIONDIR
#
tty -s || {
    if [[ ${wm} != headless ]] ; then
        start_window_manager
    fi
    if [[ ${SESSIONDIR} == "" ]] ; then
        msg="SESSIONDIR environment variable not set"
        echoerr ${msg}
        exit 1
    fi
    setup_workingdir ${SESSIONDIR}
}

# If the user specified an alternative working directory,
# Change dirs to the working directory.
if [[ ${goto_workingdir} == "true" ]] ; then
    setup_workingdir ${workingdirectory}
fi

#
# run the application...
#

start_bg_commands

echoerr "------------------------------------------------------"
echoerr "final variable assignments before execution:"
echoerr "toolname = ${toolname}"
echoerr "TOOL_VERSION = ${TOOL_VERSION}"
echoerr "TOOLDIR = ${TOOLDIR}"
echoerr "resource_tool_title = $resource_tool_title"
echoerr "resource_tool_version = $resource_tool_version"
echoerr "------------------------------------------------------"

export PROGRAM=$(substitute_command_templates "${toolcmd}")
toolargs=$(substitute_command_templates "${toolargs}")

# the "%% *" trims everything after the first space from
# the variable $PROGRAM. this value is then sent to the
# which command.

if [[ `which ${PROGRAM%% *}` ]] || [[ -x ${PROGRAM%% *} ]] ; then
   echoerr "exec'ing = \"${submit} ${PROGRAM} ${toolargs}\""
   eval ${submit} ${PROGRAM} ${toolargs}
else
   echoerr "tried to exec \"${PROGRAM} ${toolargs}\""
   msg="program named \"${PROGRAM%% *}\" does not appear to exist"
   echoerr ${msg}
   screenerr "${msg}"
   exit 1
fi

exit $?
