#!/bin/bash
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
# Also, don't forget set -x if you want to debug
set -Euo pipefail

# The start dtae and time of the script
START=$(date)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error_msg() {
    # Output an error message
    local msg="[error] ""$1"

    # We output to STDERR
    echo "$msg" 1>&2
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
info_msg() {
    # Output an error message
    local msg="[info] ""$1"

    # We output to STDOUT
    echo "$msg"
}

# The program name
echo "=== $0 ==="
info_msg "started at $START"

# The name of the node we are running on, can be useful to know just in case
# some nodes always fail
info_msg "hostname=""$HOSTNAME"

# Read in the arguments
# ARGUMENT 1: job array file name
# ARGUMENT 2: the tmp location, when running a live job this will be /Scratch
#             or ~/scratch0 depending on the cluster
# ARGUMENT 3: The step size for the job array, this is supplied so the task
#             script can use it in loops in combination with SGE_TASK_ID
# ARGUMENT 4: the task index value to run. If debugging this can be provided
#             at the command line. When running a live tasl then it will be the
#             SGE_TASK_ID environment variable of the currently running task
JA="$1"
TMP_LOC="$2"
STEP=$3
IDX=${4:-$SGE_TASK_ID}

info_msg "job array file=$JA"
info_msg "temp location=""$TMP_LOC"
info_msg "step=""$STEP"

# Make sure the job array file is set
# If $JA does not exist or it is not readable, the -z is not actually
# required as will be caught by set -u
if [[ -z "$JA" ]] || [[ ! -r "$JA" ]]; then
    error_msg "'$JA' job array file not given or is not readable"
    exit 1
fi

# Here we will put our simulated error, if the job idx is even then we will
# "error" out, that way we will require several runs of pyjasub to complete
# all the tasks, so can demonstrate how pyjasub manages the tasks
ERROR_IDX=$(awk -vtask_id=$IDX 'BEGIN{print (task_id % 2)}')

if [[ $ERROR_IDX -eq 0 ]]; then
    # "error" out
    error_msg "simulated failure job index: $IDX"
    exit 1
fi

################################################
# If we get here we will be "running" the task #
################################################

# Quit line, is used to stop sed execution after if has extracted the
# requested line from the job array file. Otherwise it will uneccessarily
# read the whole file, this is fine for small files but slow for large ones
# job array file

# Add one tot the IDX because of the header
PULL_LINE=$(( IDX + 1 ))
QUIT_LINE=$(( PULL_LINE + 1 ))
info_msg "job idx line (SGE_TASK_ID)="$IDX
info_msg "pull line (accounting for header)="$PULL_LINE
info_msg "quit line="$QUIT_LINE

# Extract the line from the file into an array
PROCESS_LINE=($(sed -n "${PULL_LINE}p;${QUIT_LINE}q" "$JA"))

# Decompose the line array, this is really for code clarity
ROW_IDX="${PROCESS_LINE[0]}"

# Whilst not seen in the example. pyjasub allows for pipe delimited input
# files and this is just accounting for that by outputting to an array
# internal spaces would be a killer though
INFILE=($(echo "${PROCESS_LINE[1]}" | tr '|' ' '))
OUTFILE="${PROCESS_LINE[2]}"

info_msg "# infiles=""${#INFILE[@]}"
info_msg "outfile="$OUTFILE

# Make sure that the the IDX == the ROWIDX it should do in all cases
# so this is a sanity check to make sure we are working on the line we think
# we are
if [[ $IDX -ne $ROW_IDX ]]; then
    error_msg "idx (SGE_TASK_ID) [${IDX}] != rowidx [${ROW_IDX}]"
    exit 1
fi

# Now we are actually going to perfom the task, this is simply catting from
# one file to another and adding some extrat text to it. Even so, we are going
# to use best practice here and do it all via a temp file, so if anything
# should fail we are not left with a half written output file. In a larger
# task script we would also use a trap to clean up any unwanted outputs

# Create a temp file template that contains our username and the job index
# use that to create a temp file
TEMPLATE=".RUN_${USER}_${IDX}_XXXXXXXXXX"
TEMP_FILE=$(mktemp -p"${TMP_LOC}" "$TEMPLATE")
info_msg "temp file=""$TEMP_FILE"

# Perform the task, in a real world scenario this is probably some application
# that you want to run or a set of applications
cat <(echo "output file: ") "$INFILE" > "$TEMP_FILE"

END=$(date)

# Now apart from ending statements the last thing we do is move our temp file
# to the final output file location
mv "$TEMP_FILE" "$OUTFILE"

# The start dtae and time of the script
info_msg "ended at $END"
echo "*** END ***"
