#!/usr/bin/bash
set -e
#set -x;

GET_DMRPP_VERSION="get_dmrpp-3.20.13"

###############################################################################
#
# Print the usage information.
#
function show_usage() {
    echo ""
    echo " [${GET_DMRPP_VERSION}]"
    cat << EOF

Usage: $0 [options] <INPUT_DATA_FILE>

Write the DMR++ for INPUT_DATA_FILE (a hdf5/netcdf-4 file) to stdout

* By default the BES Data Root directory is set to /tmp.
* This utility will add an entry to the bes.log specified in the
  configuration.
* The DMR++ is built using the DMR as returned by the HDF5 handler
  when invoked using options as set in the bes configuration the
  included BES configuration as modified by the -s option or using
  the BES configuration supplied with the -c option, see below.

-h: Show help
-z: Show version information (Verbose got here first.)
-v: Verbose: Print the DMR and process information
-V: Very Verbose: print the DMR, the command and the configuration
    files used to build the DMR and other DAP stuff.
-D: Just print the DMR that will be used to build the DMR++
-u: The binary object URL for use in the DMR++ file. If option '-u'
    is not used; then dap4:Dataset/@dmrpp:href attribute will contain
    the template string OPeNDAP_DMRpp_DATA_ACCESS_URL which can be
    replaced at runtime.
-b: The fully qualified path to the BES_DATA_ROOT directory. May not be
    "/" or "/etc". The default value is /tmp if a value is not provided.
-c: The path to the bes configuration file to use.
-s: The path to an optional addendum configuration file which will be
    appended to the default BES configuration. Much like the site.conf
    file works for the full server deployment it will be loaded last and
    the settings there-in will have an override effect on the default
    configuration.
-o: The name of the dmr++ file to create. This is required when using
    -U so that the AWS CLI component has a file to transfer.
-e: The name of pre-existing dmr++ file to test.
-T: Run ALL hyrax tests on the resulting dmr++ file and compare the
    responses the ones generated by the source hdf5 file.
-I: Run hyrax inventory tests on the resulting dmr++ file and compare the
    responses the ones generated by the source hdf5 file.
-F: Run hyrax value probe tests on the resulting dmr++ file and compare the
    responses the ones generated by the source hdf5 file.
-M: Create and merge missing CF coordinate domain variables into the dmrpp.
    If there are missing variables, a sidecar file named <INPUT_DATA_FILE>.missing
    will be created in the same directory location as the INPUT_DATA_FILE.
    If option 'p' is not used; missing variable chunk href will contain
    OPeNDAP_DMRpp_MISSING_DATA_ACCESS_URL. If option 'p' is selected; missing
    variable chunk href will contain the argument provided to that option
-p: The value to use for each missing variable's dmrpp:chunk/@dmrpp:href attribute.
    If option '-p' is not used; the missing variable dmrpp:chunk/@dmrpp:href
    attributes will contain the template string OPeNDAP_DMRpp_MISSING_DATA_ACCESS_URL
    which can be replaced at runtime.
-r: The path to the file that contains missing variable information for sets of
    input data files that share common missing variables. The file will be created
    if it doesn't exist and the result may be used in subsequent invocations of $0
    (using -r) to identify the missing variable file.
-U: If present, and if the input data file is an AWS S3 URL (s3://...), and if the
    output data file has been set (using the -o option) the presence of this parameter
    will cause get_dmrpp to copy the finished dmr++ file to the same S3 bucket location
    as the input data file. This must be used in conjunction with the -o switch as a
    local file is required before it can be transferred to S3.
-A: If present will cause the tool to use the "build" versions of the various component
    libraries as opposed to the nominal "install" version. This option is used by the
    autotest (aka make check/distcheck) targets for testing against builds that have not
    been installed.
-X: Will prevent the application from removing the temporary files. Very useful for the debugging.

Limitations:
* The name of the hdf5 file must be expressed relative to the BES_DATA_ROOT, or as an S3 URL (s3://...)

EOF
}

OPTIND=1 # Reset in case getopts has been used previously in this shell

VERBOSE=
VERY_VERBOSE=
JUST_DMR=
DMRPP_URL=
BES_CONF_FILE=
SITE_CONF_FILE=
RUN_INVENTORY_TESTS=
RUN_VALUE_TESTS=
MERGE_MISSING_VARS=
OUTPUT_FILE=
REDUCE_MISSING_FILES=
MASTER_SHA256=
BES_DATA_ROOT="/tmp"
EXISTING_DMRPP=
S3_UPLOAD=
USE_AUTOMAKE_LIBS=
CLEANUP_TEMP_FILES="true"
export TEMP_FILE_LIST=""

while getopts "h?vVDu:o:c:b:s:p:r:e:zTIFMUAX" opt; do
    case "$opt" in
    h | \?)
        show_usage >&2
        exit 0
        ;;
    z)
        echo "${GET_DMRPP_VERSION}" >&2
        exit 0
        ;;
    A)
        USE_AUTOMAKE_LIBS="true"
        ;;
    v)
        VERBOSE="yes"
        echo "#######################################################################################" >&2
        echo "# ${0}" >&2
        echo "# BEGIN (VERBOSE)" >&2
        ;;
    V)
        VERY_VERBOSE="-v"
        VERBOSE="yes"
        echo "#######################################################################################" >&2
        echo "# ${0}" >&2
        echo "# BEGIN (VERY_VERBOSE)" >&2
        ;;
    c)
        BES_CONF_FILE="$OPTARG"
        ;;
    s)
        SITE_CONF_FILE="$OPTARG"
        ;;
    b)
        BES_DATA_ROOT="$OPTARG"
        ;;
    M)
        MERGE_MISSING_VARS="yes"
        ;;
    p)
        MISSING_DATA_HREF="$OPTARG"
        ;;
    r)
        MASTER_SHA256="$OPTARG"
        REDUCE_MISSING_FILES="yes"
        ;;
    D)
        JUST_DMR="yes"
        ;;
    u)
        DMRPP_URL="$OPTARG"
        ;;
    o)
        OUTPUT_FILE="$OPTARG"
        ;;
    T)
        RUN_INVENTORY_TESTS="yes"
        RUN_VALUE_TESTS="yes"
        ;;
    I)
        RUN_INVENTORY_TESTS="yes"
        ;;
    F)
        RUN_VALUE_TESTS="yes"
        ;;
    e)
        EXISTING_DMRPP="$OPTARG"
        ;;
    U)
        S3_UPLOAD="yes"
        ;;
    X)
        CLEANUP_TEMP_FILES=
        ;;
    esac
done

shift $((OPTIND - 1))
[ "$1" = "--" ] && shift

if test -n "${VERY_VERBOSE}"; then
    set -x
fi

INPUT_DATA_FILE="${1}"

if test -n "${VERBOSE}"; then
    echo "#        OUTPUT_FILE: ${OUTPUT_FILE}" >&2
    echo -n "#           JUST_DMR: " >&2
    if test -n "${JUST_DMR}"; then
        echo "true" >&2
    else
        echo "false" >&2
    fi
fi

if test -z "${DMRPP_URL}"; then
    DMRPP_URL="OPeNDAP_DMRpp_DATA_ACCESS_URL"
fi
if test -n "${VERBOSE}"; then
    echo "#          DMRPP_URL: ${DMRPP_URL}" >&2
    echo "# CLEANUP_TEMP_FILES: ${CLEANUP_TEMP_FILES}" >&2
    echo "#" >&2
fi
# If we are running tests then we need to have the output filename for the dmr++ content.
if [ -n "${RUN_INVENTORY_TESTS}" ] || [ -n "${RUN_VALUE_TESTS}" ]; then
    if [ -z "${OUTPUT_FILE}" ] && [ -z "${EXISTING_DMRPP}" ]; then
        echo "" >&2
        echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
        echo "" >&2
        echo "OUCH! In order to run the tests the dmr++ output must be written to a file using the -o option. " >&2
        echo "" >&2
        exit 1
    fi
fi

# If we are merging missing variables then we need to have the output filename for the dmr++ content.
if [ -n "${MERGE_MISSING_VARS}" ]; then
    if [ -z "${OUTPUT_FILE}" ]; then
        echo "" >&2
        echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
        echo "" >&2
        echo "OUCH! In order to merge missing variables the dmr++ output must be written to a file using the -o option. " >&2
        echo "" >&2
        exit 1
    fi
fi

###############################################################################
#
# cleanup_temp_files()
#
# Conditionally cleans up the list of temporary files created during the
# execution of this script.
# subshell: NEVER
#
function cleanup_temp_files() {
    if test -n "${CLEANUP_TEMP_FILES}"; then
        local minus_vee=""
        if test -n "${VERBOSE}"; then
            minus_vee="-v"
            echo "# BEGIN cleanup_temp_files() + + + + + + + + + + + + + + + + + + + + + +" >&2
            echo "#" >&2
            echo "# TEMP_FILE_LIST: ${TEMP_FILE_LIST}" >&2
        fi
        rm ${minus_vee} -f ${TEMP_FILE_LIST} >&2
        if test -n "${VERBOSE}"; then
            echo "# Temporary files have been removed." >&2
            echo "# END cleanup_temp_files() - - - - - - - - - - - - - - - - - - - - - - -" >&2
        fi
    fi
}

###############################################################################
#
# make_temp_file()
# subshell: NEVER
#
#
function make_temp_file() {
    local file_prefix="${1}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN make_temp_file() + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#     file_prefix: ${file_prefix}" >&2
    fi

    local tmp_file=""
    tmp_file=$(mktemp "/tmp/${file_prefix}_XXXXXX")
    local retval=$?
    if test $? -ne 0; then
        echo "ERROR: Failed to create temporary file. prefix: ${file_prefix}" >&2
        return $retval
    fi
    if test -n "${VERBOSE}"; then echo "# tmp_file: ${tmp_file}" >&2; fi

    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${tmp_file}"
    if test -n "${VERBOSE}"; then echo "# TEMP_FILE_LIST: ${TEMP_FILE_LIST}" >&2; fi

    echo "${tmp_file}"
    export TEMP_FILE="${tmp_file}"
    if test -n "${VERBOSE}"; then
        echo "# END make_temp_file() - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return 0
}

###############################################################################
#
# mk_automake_conf()
# subshell: ALWAYS
#
# Returns the module libraries used by besstandalone when invoked by automake
# tasks like "make check" and "make distcheck". These are not the nominally
# installed libraries but rather the libs compiled by make check in the BES
# builddir.
#
function mk_automake_conf() {
    local config_doc=$(
        cat << EOF
# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# These are the buildir libraries used for running make check/distcheck
BES.module.dap=/builddir/build/BUILD/bes-3.20.13/dap/.libs/libdap_module.so
BES.module.cmd=/builddir/build/BUILD/bes-3.20.13/xmlcommand/.libs/libdap_xml_module.so
BES.module.h5=/builddir/build/BUILD/bes-3.20.13/modules/hdf5_handler/.libs/libhdf5_module.so
BES.module.dmrpp=/builddir/build/BUILD/bes-3.20.13/modules/dmrpp_module/.libs/libdmrpp_module.so
BES.module.nc=/builddir/build/BUILD/bes-3.20.13/modules/netcdf_handler/.libs/libnc_module.so
BES.module.fonc=/builddir/build/BUILD/bes-3.20.13/modules/fileout_netcdf/.libs/libfonc_module.so
# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
EOF
    )
    if test -n "${VERY_VERBOSE}"; then echo "${config_doc}" >&2; fi
    echo "${config_doc}"
}

###############################################################################
#
# mk_default_bes_conf()
# subshell: NEVER
#
# Default BES Configuration.
#
function mk_default_bes_conf() {
    if test -n "${VERBOSE}"; then
        echo "# BEGIN mk_default_bes_conf() + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
    fi

    local default_bes_conf_doc=$(
        cat << EOF
# produce DMR documents for use with build_dmrpp. The DAP, XML Command
# and HDF5 handler modules are the 'installed' ones (not the modules
# found in the build tree). jhrg 5/11/18

BES.LogName=./bes.log

BES.modules=dap,cmd,h5,dmrpp,nc,fonc

BES.module.dap=/usr/lib64/bes/libdap_module.so
BES.module.cmd=/usr/lib64/bes/libdap_xml_module.so
BES.module.h5=/usr/lib64/bes/libhdf5_module.so
BES.module.dmrpp=/usr/lib64/bes/libdmrpp_module.so
BES.module.nc=/usr/lib64/bes/libnc_module.so
BES.module.fonc=/usr/lib64/bes/libfonc_module.so

# The value "@hdf5_root_directory@" is replaced at run time.
# BES.Catalog.catalog.RootDirectory=@hdf5_root_directory@
BES.Catalog.catalog.RootDirectory=${BES_DATA_ROOT}
BES.Data.RootDirectory=/dev/null

# The order is crucial here. The dmr++ files must be associated
# with the dmrpp_module while everything else is being
# gobbled up by the greedy regex for the hdf5_handler.
# If the dmrpp.TypeMatch doesn't come first then the
# hdf5_handler will claim the dmr++ files too.
BES.Catalog.catalog.TypeMatch=dmrpp:.*\.(dmrpp)$;
BES.Catalog.catalog.TypeMatch+=h5:.*(\.bz2|\.gz|\.Z)?$;

BES.Catalog.catalog.FollowSymLinks=Yes

BES.UncompressCache.dir=/tmp/hyrax_ux
BES.UncompressCache.prefix=ux_
BES.UncompressCache.size=500

AllowedHosts+=^https?:\/\/

#-----------------------------------------------------------------------#
# FONc handler specific parameters:
#-----------------------------------------------------------------------#
#Make sure this follows the netCDF-4 enhanced model
FONc.ClassicModel=false
#Don't generate global attributes since we only
#care about the variable value information.
FONc.NoGlobalAttrs=true

#-----------------------------------------------------------------------#
# HDF5 handler specific parameters:
#-----------------------------------------------------------------------#
# EnableCF: Groom the HDF5 data to follow the CF conventions
#   (true,yes|false,no, defaults to false)
#
# HDF5 handler keys that are not the compiled-in defaults. jhrg 5/24/22
H5.EnableCheckNameClashing=true
H5.EnableCF=false

EOF
    )

    if test -n "${USE_AUTOMAKE_LIBS}"; then
        local automake_libs=$(mk_automake_conf)
        if test -n "${VERBOSE}"; then
            echo "# Using automake_libs" >&2
            if test -n "${VERY_VERBOSE}"; then
                echo "${automake_libs}" >&2
            fi
        fi
        default_bes_conf_doc=$(
            echo "${default_bes_conf_doc}"
            echo "${automake_libs}"
        )
    fi
    if test -n "${VERBOSE}"; then
        echo "# END mk_default_bes_conf() - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    echo "${default_bes_conf_doc}"

}
###############################################################################

dap4ConstraintContainerElement=$(
    cat << EOF
<bes:container name="c">
<bes:dap4constraint>${dap2_ce}</bes:dap4constraint>
</bes:container>
EOF
)
dap2ConstraintContainerElement=$(
    cat << EOF
<bes:container name="c">
<bes:constraint>${dap2_ce}</bes:constraint>
</bes:container>
EOF
)
########################################################################################################################
# mkBesCmd()
# subshell: ALWAYS
#
# Creates a BES request document for the $dap_thing (dmr, ddx, etc)
# from the $source_data_path file and sticks the command in a temp file.

function mkDAP2BesCmd() {
    local source_data_path="${1}"
    local dap_type=${2}
    local dap2_ce="${3}"
    local returnAs="${4}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN mkDAP2BesCmd() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "# source_data_path: ${source_data_path}" >&2
        echo "#         dap_type: ${dap_type} " >&2
        echo "#          dap2_ce: '${dap2_ce}'" >&2
        echo "#         returnAs: '${returnAs}'" >&2
    fi

    local noConstraintContainerElement="<bes:container name=\"c\" />"

    if test -n "${dap2_ce}"; then
        #echo "Applying DAP2 CE: '${dap2_ce}'" >&2
        container_element=$(
            cat << EOF
<bes:container name="c">
<bes:constraint>${dap2_ce}</bes:constraint>
</bes:container>
EOF
        )
    else
        container_element="${noConstraintContainerElement}"
    fi

    local besGET_ELEMENT=""
    if test -n "${returnAs}"; then
        besGET_ELEMENT=$(
            cat << EOF
</bes:define>
<bes:get type="${dap_type}" definition="d" returnAs="${returnAs}" />
</bes:request>
EOF
        )
    else
        besGET_ELEMENT=$(
            cat << EOF
</bes:define>
<bes:get type="${dap_type}" definition="d" />
</bes:request>
EOF
        )
    fi

    local bes_command=$(
        cat << EOF
<?xml version="1.0" encoding="UTF-8"?>
<bes:request xmlns:bes="http://xml.opendap.org/ns/bes/1.0#" reqID="get_dmrpp.sh">
<bes:setContext name="dap_explicit_containers">no</bes:setContext>
<bes:setContext name="errors">xml</bes:setContext>
<bes:setContext name="max_response_size">0</bes:setContext>

<bes:setContainer name="c">${source_data_path}</bes:setContainer>

<bes:define name="d" space="default">
${container_element}
${besGET_ELEMENT}

EOF
    )
    local bes_cmd_file=""
    bes_cmd_file=$(mktemp -t bes_cmd_XXXXXX)
    echo "${bes_command}" > ${bes_cmd_file}

    if test -n "${VERBOSE}"; then
        echo "#     bes_cmd_file: ${bes_cmd_file} " >&2
    fi

    if test -n "${VERY_VERBOSE}"; then
        echo "#    BES Command: " >&2
        cat "${bes_cmd_file}" >&2
    fi
    if test -n "${VERBOSE}"; then
        echo "# END mkDAP2BesCmd() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    echo "${bes_cmd_file}"
}
########################################################################################################################

########################################################################################################################
# make_bes_conf()
#
# Prepare the bes.conf file - use external if provided otherwise use the here doc.
# Returns the bes conf file name on stdout.
#
function make_bes_conf() {
    local user_supplied_conf_file="${1}"
    local site_conf_file="${2}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN make_bes_conf() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "# user_supplied_conf_file: ${user_supplied_conf_file}" >&2
        echo "#          site_conf_file: ${site_conf_file}" >&2
    fi
    local bes_conf_doc=""

    if test -n "${user_supplied_conf_file}"; then
        bes_conf_doc=$(cat ${user_supplied_conf_file})
    else
        bes_conf_doc=$(mk_default_bes_conf)
    fi

    if test -n "${site_conf_file}"; then
        if test -n "${VERBOSE}"; then
            echo "# Adding additional BES configuration from file: ${2}" >&2
        fi
        bes_conf_doc=$(
            echo "${bes_conf_doc}"
            cat "${site_conf_file}"
        )
        # TODO - Fix this so the failure of "cat" is handled
    fi

    if test -n "${VERY_VERBOSE}"; then
        echo "# Using BES Configuration: " >&2
        echo "${bes_conf_doc}" >&2
    fi

    local tmp_conf_file=""
    tmp_conf_file=$(mktemp -t bes_conf_XXXX)
    echo "${bes_conf_doc}" > ${tmp_conf_file}

    if test -n "${VERBOSE}"; then
        echo "# bes_conf_file: ${tmp_conf_file}" >&2
        echo "# "$(ls -l ${tmp_conf_file}) >&2
    fi
    if test -n "${VERY_VERBOSE}"; then
        cat ${tmp_conf_file} >&2
    fi

    if test -n "${VERBOSE}"; then
        echo "# END make_bes_conf() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi

    echo "${tmp_conf_file}"
}
########################################################################################################################

########################################################################################################################
# getDap()
# Gets the passed $dap response (dmr, ddx, etc) for the file $data_path and places the response in a temp file.
#
function mkDapRequest() {
    local bes_conf_file="${1}"
    local data_path="${2}"
    local dap_type="${3}"
    local dap_ce="${4}"
    local return_as="${5}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN mkDapRequest() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "# " >&2
        echo "#  bes_conf_file: ${bes_conf_file}" >&2
        echo "#      data_path: ${data_path}" >&2
        echo "#       dap_type: ${dap_type}" >&2
        echo "#         dap_ce: ${dap_ce}" >&2
        echo "#      return_as: ${return_as}" >&2
        echo "#" >&2
    fi

    local bes_command_file=""
    bes_command_file=$(mkDAP2BesCmd "${data_path}" "${dap_type}" "${dap_ce}" "${return_as}")
    # TODO - This TEMP_FILE_LIST change is pointless in a subshell
    # export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${bes_command_file}"

    if test -n "${VERBOSE}"; then
        echo "#  bes_command_file: ${bes_command_file}" >&2
    fi
    if test -n "${VERY_VERBOSE}"; then
        cat "${bes_command_file}" >&2
    fi

    local target_dap_file=""
    target_dap_file=$(mktemp -t ${dap_type}_${return_as}_XXXXXX)
    if test -n "${VERBOSE}"; then echo "#   target_dap_file: ${target_dap_file}" >&2; fi

    local BESCMD="besstandalone -c ${bes_conf_file} -i ${bes_command_file} -f ${target_dap_file} "
    if test -n "${VERBOSE}"; then echo "#   BESCMD: ${BESCMD}" >&2; fi
    ${BESCMD}
    status=$?
    if test -n "${CLEANUP_TEMP_FILES}"; then
        if test -n "${VERBOSE}"; then echo "## Cleaning up bes_command_file: ${bes_command_file}" >&2;  fi
        rm -f "${bes_command_file}"
    fi
    if test $status -ne 0; then
        echo "" >&2
        echo "!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!" >&2
        echo "ERROR! The besstandalone program failed to build a ${dap_type} file!." >&2
        echo "       The configuration is here: ${bes_conf_file}" >&2
        echo "             The command is here: ${bes_command_file}" >&2
        echo "The besstandalone output is here: ${target_dap_file}" >&2
        echo "     besstandalone return status: $status" >&2
        echo "!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!" >&2
        echo "" >&2
        return $status
    fi

    if test -n "${VERY_VERBOSE}"; then
        echo "# target_dap_file: " >&2
        cat "${target_dap_file}" >&2
    fi
    if test -n "${VERBOSE}"; then
        echo "# END mkDapRequest() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    # This is the return value of the function!
    echo "${target_dap_file}"

}
########################################################################################################################

########################################################################################################################
# mk_dmrpp()
# Uses build_dmrpp application to build the requested dmr++ file.
#
function mk_dmrpp() {
    local bes_conf_file="${1}"
    local datafile="${2}"
    local source_dmr_file="${3}"
    local output_filename="${4}"

    local full_data_path="${BES_DATA_ROOT}/${datafile}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN mk_dmrpp() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "#   BES_DATA_ROOT: ${BES_DATA_ROOT}" >&2
        echo "#       DMRPP_URL: ${DMRPP_URL}" >&2
        echo "#   bes_conf_file: ${bes_conf_file} " >&2
        echo "#        datafile: ${datafile}" >&2
        echo "# source_dmr_file: ${source_dmr_file}" >&2
        echo "# output_filename: ${output_filename}" >&2
        echo "#  full_data_path: ${full_data_path}" >&2
    fi

    if test -z "${JUST_DMR}"; then
        prms="${VERY_VERBOSE}"
        prms="${prms} -c ${bes_conf_file} "
        prms="${prms} -f ${full_data_path} "
        prms="${prms} -r ${source_dmr_file} "
        prms="${prms} -u ${DMRPP_URL} "
        prms="${prms} -M " # Adds the production metadata (version numbers and configuration) to the dmr++ result.
        if test -n "${VERBOSE}"; then echo "#    build_params: ${prms}" >&2; fi
        if test -n "${output_filename}"; then
            build_dmrpp ${prms} > "${output_filename}"
            status=$?
        else
            build_dmrpp ${prms}
            status=$?
        fi
        if test $status -ne 0; then
            echo "ERROR: build_dmrpp ${prms} FAILED! status: $status"
            return $status
        fi
    else
        echo "The JUST_DMR flag is set, skipping dmr++ construction."
    fi

    if test -n "${VERBOSE}"; then
       echo "# END mk_dmrpp() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi

    return 0
}

#######################################################################################################################
# chk_dmrpp_for_missing_vars()
# Uses check_dmrpp application to identify missing variables in the requested dmr++ file.
#
function chk_dmrpp_for_missing_vars() {
    local dmrpp_file="${1}"
    local mvars_file="${2}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN chk_dmrpp_for_missing_vars() + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "#     dmrpp_file: ${dmrpp_file}" >&2
        echo "#     mvars_file: ${mvars_file}" >&2
    fi

    local retval=""

    ck_prms="${dmrpp_file} ${mvars_file}"
    if test -n "${VERBOSE}"; then echo "#   check_dmrpp parameters: ${ck_prms}" >&2; fi
    if test -n "${mvars_file}"; then
        check_dmrpp ${ck_prms}
        retval=$?
    else
        echo "The missing_vars file wasn't specified, skipping missing variable construction." >&2
        retval=1
    fi
    if test -n "${VERBOSE}"; then
        echo "# END chk_dmrpp_for_missing_vars() - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval

}
###############################################################################

###############################################################################
#
# BES Configuration elements to generate missing variable HDF5 sidecar file.
#

function make_missing_conf() {
    local user_supplied_conf_file="${1}"
    local site_conf_file="${2}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN make_missing_conf() + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "# user_supplied_conf_file: ${user_supplied_conf_file}" >&2
        echo "#          site_conf_file: ${site_conf_file}" >&2
    fi

    local missing_conf_elements=$(
        cat << EOF
# Make sure this follows the netCDF-4 enhanced model
#
FONc.ClassicModel=false
#
# Don't generate global attributes since we only
# care about the variable value information.
#
FONc.NoGlobalAttrs=true
H5.ForceFlattenNDCoorAttr=true
H5.RmConventionAttrPath=true
#
# We don't map 64-bit integer to DMR
# to make it consistent with data value reading.
#
H5.EnableDMR64bitInt=false
#
# When the HDF5 storage size is zero, Fullnamepath
# attribute should not be generated for DMRPP to work.
#
H5.NoZeroSizeFullnameAttr=true
#
EOF
    )

    local bes_conf_doc=""
    if test -n "${user_supplied_conf_file}"; then
        if test -n "${VERBOSE}"; then
            echo "# Using: \"${user_supplied_conf_file}\"" >&2
        fi
        bes_conf_doc=$(cat ${user_supplied_conf_file})
    else
        bes_conf_doc=$(mk_default_bes_conf)
    fi

    bes_conf_doc=$(
        echo "${bes_conf_doc}"
        echo "${missing_conf_elements}"
    )

    if test -n "${site_conf_file}"; then
        if test -n "${VERBOSE}"; then
            echo "# Adding additional BES configuration from file: ${2}" >&2
        fi
        bes_conf_doc=$(
            echo "${bes_conf_doc}"
            cat "${site_conf_file}"
        )
    fi

    local missing_bes_conf_file=$(mktemp -t missing_vars_bes_conf_XXXX)

    echo "${bes_conf_doc}" > ${missing_bes_conf_file}

    if test -n "${VERY_VERBOSE}"; then
        echo "# missing_bes_conf_file: ${missing_bes_conf_file}" >&2
        echo "# "$(ls -l ${missing_bes_conf_file}) >&2
        if test -n "${VERY_VERBOSE}"; then
            cat ${missing_bes_conf_file} >&2
        fi
    fi

    if test -n "${VERBOSE}"; then
        echo "# END make_missing_conf() - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    echo "${missing_bes_conf_file}"

}
###############################################################################

#######################################################################################################################
# merge_dmrpp_missing()
# Uses merge_dmrpp application to identify missing variables in the requested dmr++ file.
#
function merge_dmrpp_missing() {
    local missing_vars_dmrpp_file="${1}"
    local master_dmrpp_file="${2}"
    local missing_data_url="${3}"
    local missing_vars_file="${4}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN merge_dmrpp_missing() + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "# missing_vars_dmrpp_file: ${missing_vars_dmrpp_file}" >&2
        echo "#       master_dmrpp_file: ${master_dmrpp_file}" >&2
        echo "#        missing_data_url: ${missing_data_url}" >&2
        echo "#       missing_vars_file: ${missing_vars_file}" >&2
    fi

    mg_prms="${mg_prms} ${missing_vars_dmrpp_file} "
    mg_prms="${mg_prms} ${master_dmrpp_file} "
    mg_prms="${mg_prms} ${missing_data_url}"
    mg_prms="${mg_prms} ${missing_vars_file} "
    if test -n "${VERBOSE}"; then echo "#   build_params: ${mg_prms}" >&2; fi
    local retval=""
    if test -n "$missing_vars_file"; then
        merge_dmrpp ${mg_prms}
        retval=$?
    else
        echo "The missing_vars file doesn't exist, there are no missing variables to merge." >&2
        retval=1
    fi

    if test -n "${VERBOSE}"; then
        echo "# END merge_dmrpp_missing() - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval
}
########################################################################################################################

#######################################################################################################################
# reduce_dmrpp()
# Uses reduce_dmrpp application to identify missing variables in the requested dmr++ file.
#
function reduce_dmrpp() {
    local missing_vars_data_file="${1}"
    local missing_vars_dmrpp_file="${2}"
    local master_sha256="${3}"
    local temporary_storage_file="${4}"

    local mvdf_full_path="${BES_DATA_ROOT}/${missing_vars_data_file}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN reduce_dmrpp() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "#  missing_vars_data_file: ${missing_vars_data_file}" >&2
        echo "# missing_vars_dmrpp_file: ${missing_vars_dmrpp_file}" >&2
        echo "#          mvdf_full_path: ${mvdf_full_path}" >&2
        echo "#           master_sha256: ${master_sha256}" >&2
        echo "#  temporary_storage_file: ${temporary_storage_file}" >&2
        echo "#" >&2
    fi

    rd_prms="${rd_prms} ${missing_vars_dmrpp_file} "
    rd_prms="${rd_prms} ${mvdf_full_path} "
    rd_prms="${rd_prms} ${master_sha256}"
    rd_prms="${rd_prms} ${temporary_storage_file} "
    if test -n "${VERBOSE}"; then echo "#   build_params: ${rd_prms}" >&2; fi

    local retval=""
    if test -n "${missing_vars_dmrpp_file}"; then
        reduce_mdf ${rd_prms}
        retval=$?
    else
        echo "The missing_vars_dmrpp_file was not set exist, there are no missing variables to merge." >&2
        retval=1
    fi
    if test -n "${VERBOSE}"; then
        echo "# END reduce_dmrpp() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval

}
########################################################################################################################

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

########################################################################################################################
# dap2_vars()
# Prints a list of DAP2 DDX variable declarations found in the file $1
# The leading and trailing whitespace is trimmed from each line, and the result is sorted.
#
function dap2_vars() {
    grep -e "<Byte" \
        -e "<Int" \
        -e "<UInt" \
        -e "<Float" \
        -e "<Float" \
        -e "<String" \
        -e "<URL" \
        -e "<Grid" \
        -e "<Sequence" \
        -e "<Structure" \
        -e "<Array" \
        "${1}" | sed 's/ *$//g' | sort
}
########################################################################################################################

########################################################################################################################
# dap2_attributes()
# Prints a list of DAP2 DDX Attribute declarations found in the file $1
# The leading and trailing whitespace is trimmed from each line, and the result is sorted.
#
function dap2_attributes() {
    grep -e "<Attribute name=" -e "<Value>" "${1}" | sed 's/ *$//g' | sort
}
########################################################################################################################

########################################################################################################################
# dap4_attributes()
# Prints a list of DAP4 DMR Attribute declarations found in the file $1
# The leading and trailing whitespace is trimmed from each line, and the result is sorted.
#
function dap4_attributes() {
    grep -e "<Attribute name=" -e "<Value>" "${1}" | sed 's/ *$//g' | sort
}
########################################################################################################################

########################################################################################################################
# dap4_vars()
# Prints a list of DAP4 DMR variable declarations found in the file $1
# The sed command:
# - removes leading and trailing whitespace from each line
# - removes both /> and > from each line because making the dmr++ may add chunks (a kind of metadata) to
#   variables that had no metedata (Attributes) previously, thus their termination may legitimately change
#   from /> to >
# The output of the sed command is sorted to normalize changes in order caused by the creation of the dmr++.
#
function dap4_vars() {
    grep \
        -e "<Int8" \
        -e "<UInt8" \
        -e "<Byte" \
        -e "<Char" \
        -e "<Int" \
        -e "<UInt" \
        -e "<Float" \
        -e "<String" \
        -e "<URI" \
        -e "<Enumeration" \
        -e "<Sequence" \
        -e "<Structure" \
        -e "<Array" \
        -e "<Group" \
        -e "<Dimension" \
        "${1}" | sed -e 's/ *$//g' -e 's+/>++g' -e 's+>++g'| sort
}
########################################################################################################################

########################################################################################################################
# dap4_vars()
# Prints a list of DAP4 DMR variable declarations found in the file $1
# The leading and trailing whitespace is trimmed from each line, and the result is sorted.
#
function dap4_array_counter() {
    dmr_file=${1}
    type=${2}

    #echo "    dmr_file: ${dmr_file}" >&2;
    #echo "   DAP4 type: ${type}" >&2;

    cat "${dmr_file}" | awk -v type=${type} 'BEGIN{
    inVar = 0;
    typeMatchString = "^ *<"type;
    closeMatchString = "^ *</"type;
    match_count=0;
    }
    {
    if( match($0,typeMatchString)==1){
    inVar=1;
    match_count++;
    }
    if( inVar>0){
    if( match($0,closeMatchString)==1){
    inVar=0;
    }
    }
    }END{print match_count;}' -

}
########################################################################################################################

function dap4_array_dim_counter() {
    dmr_file=${1}
    type=${2}
    match_num="${3}"
    awk_params="-v type=${type} -v match_num=${match_num}"

    cat "${dmr_file}" | awk ${awk_params} 'BEGIN{
    inVar = 0;
    typeMatchString = "^ *<"type;
    # print "typeMatchString: " typeMatchString;
    closeMatchString = "^ *</"type
    # print "closeMatchString: " closeMatchString;
    target_dim_count=0;
    match_count=0;
    target_var_name="not found";
    }
    {
    if( match($0,typeMatchString)==1){
    inVar=1;
    match_count++;
    if(match_count==match_num){
    # <Float32 name="ClrOLR_A">
    target_var_name=$2;
    sub("name=\"","",target_var_name);
    sub("\">","",target_var_name);
    }
    }
    if( inVar>0){
    if(match_count==match_num &&  match($0,"^ *<Dim")==1  ){
    target_dim_count++;
    }
    else if( match($0,closeMatchString)==1){
    inVar=0;
    }
    }
    }END{ print target_var_name, target_dim_count; }' -

}
########################################################################################################################

########################################################################################################################
# dap4_vars()
# Prints a list of DAP4 DMR variable declarations found in the file $1
# The leading and trailing whitespace is trimmed from each line, and the result is sorted.
#
function dap4_array_test() {
    local bes_conf_file="${1}"
    local dmr_file="${2}"
    local target_file="${3}"
    local type="${4}"
    if test -n "${VERBOSE}"; then
        echo "# BEGIN dap4_array_test() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#" >&2
        echo "# bes_conf_file: ${bes_conf_file}" >&2;
        echo "#      dmr_file: ${dmr_file}" >&2;
        echo "#   target_file: ${target_file}" >&2;
        echo "#     DAP4 type: ${type}" >&2;
        echo "#" >&2
    fi
    local var_count=""
    var_count=$(dap4_array_counter ${dmr_file} ${type})
    if test -n "${VERBOSE}"; then echo "# Found ${var_count} variables of type ${type} in DMR: ${dmr_file}" >&2; fi
    if [ ${var_count} -lt 1 ]; then
        echo "ERROR! No variables of type ${type} found in DMR!" >&2
        return 0
    fi

    local pick_var=""
    pick_var=$(echo "${var_count}*${RANDOM}/32767+1" | bc)
    if test -n "${VERBOSE}"; then echo "# Picked variable: ${pick_var}" >&2; fi

    local name_and_dim=""
    name_and_dim=$(dap4_array_dim_counter ${dmr_file} ${type} ${pick_var})
    if test -n "${VERY_VERBOSE}"; then echo "# name_and_dim: ${name_and_dim}" >&2; fi

    local var_name=""
    var_name=$(echo "${name_and_dim}" | awk '{print $1;}' -)
    if test -n "${VERY_VERBOSE}"; then echo "# var_name: ${var_name}" >&2; fi

    local var_dims=""
    var_dims=$(echo "${name_and_dim}" | awk '{print $2;}' -)
    if test -n "${VERY_VERBOSE}"; then echo "# var_dims: ${var_dims}" >&2; fi

    local query_string="${var_name}"
    for ((i = 1; i <= ${var_dims}; i++)); do
        query_string=${query_string}"[0]"
    done
    if test -n "${VERBOSE}"; then echo "#   query_string: ${query_string}" >&2; fi

    local bes_cmd_file=""
    bes_cmd_file=$(mkDAP2BesCmd "${INPUT_DATA_FILE}" "dods" "${query_string}")
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${bes_cmd_file}"
    if test -n "${VERBOSE}"; then echo "# bes_cmd_file: ${bes_cmd_file}" >&2; fi

    local dap2_source_response=""
    dap2_source_response=$(mktemp -t dap2_array_test_source_response_XXXX)
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${dap2_source_response}"
    if test -n "${VERBOSE}"; then echo "# dap2_source_response: ${dap2_source_response}" >&2; fi

    besstandalone -c "${bes_conf_file}" -i "${bes_cmd_file}" > ${dap2_source_response}
    if test -n "${VERBOSE}"; then echo "# dap2_source_response: ${dap2_source_response}" >&2; fi
    if test -n "${VERY_VERBOSE}"; then getdap -D -M "${dap2_source_response}" >&2; fi

    local bes_cmd_file=""
    bes_cmd_file=$(mkDAP2BesCmd "${target_file}" "dods" "${query_string}")
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${bes_cmd_file}"
    if test -n "${VERBOSE}"; then echo "# bes_cmd_file: ${bes_cmd_file}" >&2; fi

    local dap2_dmrpp_response=""
    dap2_dmrpp_response=$(mktemp -t dap2_array_test_dmrpp_response_XXXX)
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${dap2_dmrpp_response}"
    if test -n "${VERBOSE}"; then echo "# dap2_dmrpp_response: ${dap2_dmrpp_response}" >&2; fi

    besstandalone -c "${bes_conf_file}" -i "${bes_cmd_file}" > ${dap2_dmrpp_response}
    if test -n "${VERY_VERBOSE}"; then getdap -D -M ${dap2_dmrpp_response} >&2; fi

    local retval=""
    local status=""

    if test -n "${VERBOSE}"; then echo "# Comparing binary responses..." >&2; fi
    cmp "${dap2_source_response}" "${dap2_dmrpp_response}" >&2
    status=$?
    if test $status -eq 0 ; then
        if test -n "${VERBOSE}"; then echo "# Responses are the same. w00t" >&2; fi
        retval $status
    else
        if test -n "${VERBOSE}"; then echo "# ERROR! Responses differ." >&2; fi
        retval=100
    fi
    if test -n "${VERBOSE}"; then
        echo "# END dap4_array_test() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval

}

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

########################################################################################################################
# compare_files()
# Compares two files by first applying the check_function to each file and then comparing the results of the
# check_function using diff.
#
function compare_files() {
    local check_function=${1}
    local first_file=${2}
    local second_file=${3}
    local success_msg="${4}"
    local fail_msg="${5}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN compare_files() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "# check_function: ${check_function}" >&2
        echo "#     first_file: ${first_file}" >&2
        echo "#    second_file: ${second_file}" >&2
        echo "#    success_msg: ${success_msg}" >&2
        echo "#       fail_msg: ${fail_msg}" >&2
    fi

    local first_result=""
    first_result=$(mktemp -t "${check_function}_result_1_XXXX")
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${first_result}"
    if test -n "${VERBOSE}"; then
        echo "#   first_result: ${first_result}" >&2
    fi
    f_stuff=$(${check_function} "${first_file}")
    echo "${f_stuff}" > "${first_result}"

    second_result=$(mktemp -t "${check_function}_result_2_XXXX")
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${second_result}"
    if test -n "${VERBOSE}"; then
        echo "#  second_result: ${second_result}" >&2
    fi
    s_stuff=$(${check_function} "${second_file}")
    echo "${s_stuff}" > "${second_result}"

    local retval=0
    diff --ignore-space-change --brief "${first_result}" "${second_result}"
    if [ $? -eq 0 ]; then
        if test -n "${VERBOSE}"; then
            echo "# RESULT: ${success_msg}" >&2
        fi
    else
        echo "${fail_msg}" >&2
        retval=1
    fi
    if test -n "${VERBOSE}"; then
        echo "# END compare_files() retval: $retval - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval
}
########################################################################################################################

########################################################################################################################
# dmrpp_inventory_test()
#
function dmrpp_inventory_test() {
    local bes_conf_file="${1}"
    local source_dmr_file="${2}"
    local output_filename="${3}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN dmrpp_inventory_test() + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#   bes_conf_file: ${bes_conf_file}" >&2
        echo "# source_dmr_file: ${source_dmr_file}" >&2
        echo "# output_filename: ${output_filename}" >&2
        echo "#" >&2
    fi

    if [[ ${output_filename} == ${BES_DATA_ROOT}* ]]; then
        target_file=$(echo "${output_filename}" | sed -e "s+${BES_DATA_ROOT}++")
    else
        test_file="${BES_DATA_ROOT}/"$(basename ${output_filename})
        if test ! -f ${test_file}; then
            cp ${output_filename} ${BES_DATA_ROOT}
            export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${test_file}"
        fi
        target_file=$(basename ${output_filename})
    fi

    if test -n "${EXISTING_DMRPP}"; then
        target_file="${EXISTING_DMRPP}"
    fi

    if test -n "${VERBOSE}"; then
        echo "# INPUT_DATA_FILE: ${INPUT_DATA_FILE}" >&2
        echo "# TARGET_DATAFILE: ${target_file}" >&2
    fi

    local status=0
    local retval=0

    if test -n "${VERBOSE}"; then
        echo "# Comparing DMR responses for:" >&2
        echo "# "$(ls -l "${source_dmr_file}") >&2
        echo "# "$(ls -l "${target_file}") >&2
    fi
    compare_files dap4_vars "${source_dmr_file}" "${target_file}" "DAP4 variables MATCHED." "ERROR! DAP4 variables mismatch!"
    status=$?
    if test -n "${VERBOSE}"; then echo "# compare_files dap4_vars, status: ${status}" >&2 ; fi
    retval=$(echo "${retval} + ${status}" | bc)

    if test -n "${VERBOSE}"; then
        echo "# END dmrpp_inventory_test() retval: $retval - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval
}

########################################################################################################################
# dmrpp_value_test()
#
function dmrpp_value_test() {
    local bes_conf_file="${1}"
    local source_dmr_file="${2}"
    local output_filename="${3}"

    if test -z "${EXISTING_DMRPP}"; then
        target_file="${output_filename}"
    else
        target_file="${EXISTING_DMRPP}"
    fi

    if test -n "${VERY_VERBOSE}"; then
        echo "# BEGIN dmrpp_value_test() + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "# INPUT_DATA_FILE: ${INPUT_DATA_FILE}" >&2
        echo "#   bes_conf_file: ${bes_conf_file}" >&2
        echo "#     target_file: ${target_file}" >&2
        echo "# source_dmr_file: ${source_dmr_file}" >&2
    fi
    local retval=0
    local status=""

    for datatype in Int8 UInt8 Int16 UInt16 Int32 UInt32 Float32 Float64; do
        dap4_array_test "${bes_conf_file}" "${source_dmr_file}" "${target_file}" "${datatype}"
        status=$?
        retval=$(echo "${retval} + ${status}" | bc)
    done
    if test -n "${VERY_VERBOSE}"; then
        echo "# END dmrpp_value_test() - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval
}

########################################################################################################################
# check_aws_config()
#
# Verify that the AWS CLI has been configured with credentials and default region
# Returns non-zero if the AWS CLI is not configured
#
function check_aws_config() {
    if test -n "${VERBOSE}"; then
        echo "# BEGIN check_aws_config() + + + + + + + + + + + + + + + + + + + + + + +" >&2
    fi
    local retval=0
    aws configure list | grep "access_key" | grep -v "<not set>" > /dev/null
    retval=$?
    if test $retval -ne 0; then
        echo "ERROR - The AWS_ACCESS_KEY_ID is not set." >&2
    else
        aws configure list | grep "secret_key" | grep -v "<not set>" > /dev/null
        retval=$?
        if test $retval -ne 0; then
            echo "ERROR - The AWS_SECRET_ACCESS_KEY is not set." >&2
        else
            aws configure list | grep "region" | grep -v "<not set>" > /dev/null
            retval=$?
            if test $retval -ne 0; then
                echo "WARNING - The AWS_DEFAULT_REGION is not set." >&2
                retval=0
            fi
        fi
    fi
    if test -n "${VERBOSE}"; then
        echo "# END check_aws_config() - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval

}

########################################################################################################################
# merge_missing()
#
# This function merges the missing variables (or not, depending on the state of
# the global MERGE_MISSING_VARS env var.)
#
function merge_missing() {
    local bes_conf_file="${1}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN merge_missing() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#     bes_conf_file: ${bes_conf_file}" >&2
        echo "#" >&2
    fi

    local status=0
    if test -n "${MERGE_MISSING_VARS}"; then

        # use $PATH/check_dmrpp to query dmrpp for missing CF domain variables [requires ${OUTPUT_FILE}; -o option]
        local missing_vars_file=""
        missing_vars_file=$(mktemp -t missing_vars_XXXX)
        export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${missing_vars_file}"
        chk_dmrpp_for_missing_vars "${OUTPUT_FILE}" "${missing_vars_file}"
        status=$?
        if test -n "${VERBOSE}"; then echo "# chk_dmrpp_for_missing_vars status: ${status}" >&2; fi
        if test $status -ne 0; then
            echo "ERROR: chk_dmrpp_for_missing_vars ${OUTPUT_FILE} ${missing_vars_file} FAILED! status: ${status}" >&2
            return $status
        fi

        # check-dmrpp will write to the missing_vars_file if there are any missing variables.
        if test -s ${missing_vars_file}; then
            if test -n "${VERBOSE}"; then echo "# The missing variables file: ${missing_vars_file} contains bytes." >&2; fi
            local missing_variables=""
            missing_variables=$(cat ${missing_vars_file})
            if test -n "${VERBOSE}"; then echo "# Identified the missing variables: ${missing_variables}" >&2; fi

            # augment bes_conf_file with additional H5 parameters necessary to synthesize missing_variables from INPUT_DATA_FILE.
            local mk_missing_conf_file=""
            mk_missing_conf_file=$(make_missing_conf "${bes_conf_file}" "${SITE_CONF_FILE}")

            # insert '_missing' into INPUT_DATA_FILE name and use for SIDECAR_MISSING_FILE name
            SIDECAR_MISSING_FILE="${INPUT_DATA_FILE}.missing"
            if test -n "${VERBOSE}"; then echo "#  Missing variables data file: ${SIDECAR_MISSING_FILE}" >&2; fi

            # use besstandalone to generate SIDECAR_MISSING_FILE
            local sidecar_missing_temp_file=""
            sidecar_missing_temp_file=$(mkDapRequest "${mk_missing_conf_file}" "${INPUT_DATA_FILE}" dods "${missing_variables}" netcdf-4)
            status=$?
            if test -n "${VERBOSE}"; then echo "# mkDapRequest status: ${status}" >&2; fi
            if test ${status} -ne 0; then
                echo "ERROR: mkDapRequest ${mk_missing_conf_file}  ${INPUT_DATA_FILE} dods ${missing_variables} netcdf-4 FAILED! status: $status" >&2
                return $status
            fi
            # Because we move the temp file there is no need to add it to TEMP_FILE_LIST
            mv "${sidecar_missing_temp_file}" "${BES_DATA_ROOT}/${SIDECAR_MISSING_FILE}"

            if test -n "${VERBOSE}"; then echo "# "$(ls -l ${SIDECAR_MISSING_FILE}); fi

            local bes_conf_file=""
            bes_conf_file=$(make_bes_conf "${bes_conf_file}" "${SITE_CONF_FILE}")
            export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${bes_conf_file}"

            local missing_vars_dmr_file=""
            missing_vars_dmr_file=$(mkDapRequest ${bes_conf_file} ${SIDECAR_MISSING_FILE} dmr) # use besstandalone to generate dmr for SIDECAR_MISSING_FILE
            status=$?
            if test -n "${VERBOSE}"; then echo "# mkDapRequest status: ${status}" >&2; fi
            if test ${status} -ne 0; then
                echo "ERROR: mkDapRequest() ${bes_conf_file} ${SIDECAR_MISSING_FILE} dmr  FAILED! status: $status" >&2
                return $status
            fi
            export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${missing_vars_dmr_file}"

            master_dmrpp_file="${OUTPUT_FILE}" # save original dmrpp filename

            local missing_dmrpp_file=""
            if test -n "${REDUCE_MISSING_FILES}"; then
                if test ! -s ${MASTER_SHA256}; then
                    missing_dmrpp_file="${BES_DATA_ROOT}/${SIDECAR_MISSING_FILE}.dmrpp"
                else
                    missing_dmrpp_file=$(mktemp -t missing_vars_dmrpp_XXXXXX) # generate temporary dmrpp filename for SIDECAR_MISSING_FILE
                fi
            else
                missing_dmrpp_file=$(mktemp -t missing_vars_dmrpp_XXXXXX) # generate temporary dmrpp filename for SIDECAR_MISSING_FILE
            fi
            export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${missing_dmrpp_file}"

            mk_dmrpp "${bes_conf_file}" "${SIDECAR_MISSING_FILE}" "${missing_vars_dmr_file}" "${missing_dmrpp_file}" # use $PATH/build_dmrpp to generate dmrpp for SIDECAR_MISSING_FILE
            status=$?
            if test -n "${VERBOSE}"; then echo "# mk_dmrpp status: ${status}" >&2; fi
            if test $status -ne 0; then
                echo "ERROR: mk_dmrpp  ${bes_conf_file} ${SIDECAR_MISSING_FILE} ${missing_vars_dmr_file} ${missing_dmrpp_file} FAILED! status: $status" >&2
                return $status
            fi

            if test -n "${REDUCE_MISSING_FILES}"; then
                local tempstore_file=""
                tempstore_file=$(mktemp -t tmpstore_XXXX) # generate temporary filename for tempstore to hold missing_dmrpp info
                export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${tempstore_file}"
                if test -n "${VERBOSE}"; then
                    echo "#        MASTER_SHA256: ${MASTER_SHA256}" >&2
                    echo "#   missing_dmrpp_file: ${missing_dmrpp_file}" >&2
                    echo "# sidecar_missing_file: ${SIDECAR_MISSING_FILE}" >&2
                    echo "#       tempstore_file: ${tempstore_file}" >&2
                fi

                # use $PATH/reduce_mdf to merge SIDECAR_MISSING_FILE dmrpp with master_dmrpp_file
                reduce_dmrpp "${SIDECAR_MISSING_FILE}" "${missing_dmrpp_file}" "${MASTER_SHA256}" "${tempstore_file}"
                status=$?
                if test -n "${VERBOSE}"; then echo "# reduce_dmrpp status: ${status}" >&2; fi
                if test $status -ne 0; then
                    echo "ERROR: reduce_dmrpp ${SIDECAR_MISSING_FILE} ${missing_dmrpp_file} ${MASTER_SHA256} ${tempstore_file}  FAILED! status: $status" >&2
                    return $status
                fi
                #echo "reduce_dmrpp: ${retval}"

                if test -s ${tempstore_file}; then
                    local temp_sidecar_dmrpp=${missing_dmrpp_file}
                    local temp_sidecar_file="${BES_DATA_ROOT}/${SIDECAR_MISSING_FILE}"
                    missing_dmrpp_file=$(awk -F'[ ]' '{print $2}' "${tempstore_file}")
                fi

            fi

            if test -n "${VERBOSE}"; then
                echo "#   missing_dmrpp_file: ${missing_dmrpp_file}" >&2
                echo "#    master_dmrpp_file: ${master_dmrpp_file}" >&2
                echo "# sidecar_missing_file: ${SIDECAR_MISSING_FILE}" >&2
                echo "#    missing_vars_file: ${missing_vars_file}" >&2
            fi

            # set 'href' content to use when merging chunks from SIDECAR_MISSING_FILE dmrpp into master_dmrpp_file
            local missing_data_url_path="OPeNDAP_DMRpp_MISSING_DATA_ACCESS_URL"
            if test -n "${MISSING_DATA_HREF}"; then
                missing_data_url_path="${MISSING_DATA_HREF}"
            fi
            # use $PATH/merge_dmrpp to merge SIDECAR_MISSING_FILE dmrpp with master_dmrpp_file
            merge_dmrpp_missing "${missing_dmrpp_file}" "${master_dmrpp_file}" "${missing_data_url_path}" "${missing_vars_file}"
            status=$?
            if test -n "${VERBOSE}"; then echo "# merge_dmrpp_missing status: ${status}" >&2; fi
            if test $status -ne 0; then
                echo "ERROR: merge_dmrpp_missing ${missing_dmrpp_file} ${master_dmrpp_file} ${missing_data_url_path} ${missing_vars_file} FAILED! status: $status" >&2
                return $status
            fi

        else
            echo " There are no missing variables to merge."
        fi
    else
        if test -n "${VERBOSE}"; then
            echo "# merge_missing() is DISABLED" >&2
        fi
   fi
    if test -n "${VERBOSE}"; then
        echo "# END merge_missing() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi

    return 0
}

########################################################################################################################
# run_tests()
#
# This function conditionally runs the dmr++ sanity checking tests
#
function run_tests() {
    local bes_conf_file="${1}"
    local source_dmr_file="${2}"
    local output_filename="${3}"

    if test -n "${VERBOSE}"; then
        echo "# BEGIN run_tests() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" >&2
        echo "#     bes_conf_file: ${bes_conf_file}" >&2
        echo "#   source_dmr_file: ${source_dmr_file}" >&2
        echo "#   output_filename: ${output_filename}" >&2
    fi

    local retval=0
    local status=0
    local tests_run=0
    if test -n "${RUN_INVENTORY_TESTS}"; then
        dmrpp_inventory_test "${bes_conf_file}" "${source_dmr_file}" "${output_filename}"
        status=$?
        echo "# Computing retval from status: $status" >&2
        # retval=`expr $retval + $status`
        # let retval=retval+status
        retval=$(echo "${retval} + ${status}" | bc)
        echo "# retval $retval" >&2
        # tests_run=`expr $tests_run + 1`
        # let tests_run+=1
        tests_run=$(echo "${tests_run} + 1" | bc)
        echo "# tests_run $tests_run" >&2
    fi

    if test -n "${RUN_VALUE_TESTS}"; then
        dmrpp_value_test "${bes_conf_file}" "${source_dmr_file}" "${output_filename}"
        status=$?
        retval=$(echo "${retval} + ${status}" | bc)
        tests_run=$(echo "${tests_run} + 1" | bc)

    fi
    if test -n "${VERBOSE}"; then
        echo "# Ran ${tests_run} tests." >&2
        echo "# END run_tests() retval: $retval - - - - - - - - - - - - - - - - - - - - - - - - - - -" >&2
    fi
    return $retval

}

########################################################################################################################
########################################################################################################################
########################################################################################################################
########################################################################################################################
#
# main() (such as it is in bashlandia)
#
#
#

function main() {
    trap cleanup_temp_files EXIT

    if test -n "${VERBOSE}"; then
        echo "#######################################################################################" >&2
        echo "# BEGIN main()" >&2
        echo "#" >&2
    fi
    local status=0
    # Here we test to see if the input file name is actually an S3 URL
    # and if it is we adjust the INPUT_DATA_FILE and go out and get the
    # object from S3.
    local s3_granule=
    if [[ "${INPUT_DATA_FILE}" =~ ^s3:\/\/.* ]]; then
        s3_granule="${INPUT_DATA_FILE}"
        echo "# Detected S3 URL as data input: ${s3_granule}" >&2

        set +e
        check_aws_config
        status=$?
        if test $status -ne 0; then
            echo "ERROR - The AWS CLI is not configured. Exiting." >&2
            return $status
        fi

        INPUT_DATA_FILE=$(basename "${s3_granule}")
        echo "# INPUT_DATA_FILE: ${INPUT_DATA_FILE}" >&2

        local s3_local_file=""
        if [[ "${BES_DATA_ROOT}" == */ ]]; then # Ends with /
            s3_local_file="${BES_DATA_ROOT}${INPUT_DATA_FILE}"
        else
            s3_local_file="${BES_DATA_ROOT}/${INPUT_DATA_FILE}"
        fi

        echo "# Copying S3 object to: ${s3_local_file}" >&2

        aws s3 cp ${s3_granule} ${s3_local_file}
        status=$?
        if test $status -ne 0; then
            echo "ERROR - Failed to acquire S3 object. (status: $status) Exiting..." >&2
            return $status
        fi

        local s3_local_side_car_missing_file="${s3_local_file}.missing"
        echo "# Missing file (if needed): ${s3_local_side_car_missing_file}" >&2
        set -e
    fi
    if test -n "${VERBOSE}";then
        echo "#######################################################################################" >&2
        echo "# BUILDING INITIAL DMR" >&2
        echo "#" >&2
    fi

    local bes_conf_file=""
    bes_conf_file=$(make_bes_conf "${BES_CONF_FILE}" "${SITE_CONF_FILE}")
    export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${bes_conf_file}"

    local dmr_file=""
    dmr_file=$(mkDapRequest "${bes_conf_file}" "${INPUT_DATA_FILE}" dmr)

    # If 'JUST_DMR' is set send the $dmr_file to either <stdout> or $OUTPUT_FILE
    if test -n "${JUST_DMR}"; then
        if test -z "${OUTPUT_FILE}"; then
            cat "${dmr_file}" 
        else
            cat "${dmr_file}" > "${OUTPUT_FILE}"
        fi
        return 0
    else
        export TEMP_FILE_LIST="${TEMP_FILE_LIST} ${dmr_file}"
    fi

    if test -n "${VERBOSE}";then
        echo "#######################################################################################" >&2
        echo "# BUILDING DMR++ (build_dmrpp)" >&2
        echo "#" >&2
    fi

    mk_dmrpp "${bes_conf_file}" "${INPUT_DATA_FILE}" "${dmr_file}" "${OUTPUT_FILE}"
    status=$?
    if test $status -ne 0; then
        echo "ERROR: mk_dmrpp() ${bes_conf_file} ${INPUT_DATA_FILE} ${dmr_file} ${OUTPUT_FILE} Failed!. status: $status" >&2
        return $status
    fi

    # if test -n "${VERBOSE}"; then echo "main() - TEMP_FILE_LIST: ${TEMP_FILE_LIST}" >&2; fi

    if test -n "${VERBOSE}";then
        echo "#######################################################################################" >&2
        echo "# MERGE MISSING DATA (CONDITIONAL)" >&2
        echo "#" >&2
    fi
    # Merge the missing variable data (conditional)
    merge_missing "${bes_conf_file}"
    status=$?
    if test $status -ne 0; then
        echo "ERROR: merge_missing() FAILED!. status: $status" >&2
        return $status
    fi

    if test -n "${VERBOSE}";then
        echo "#######################################################################################" >&2
        echo "# RUNNING INVENTORY TESTS (CONDITIONAL)" >&2
        echo "#" >&2
    fi
    # Run the dmr++ tests (conditional)
    run_tests "${bes_conf_file}" "${dmr_file}" "${OUTPUT_FILE}"
    status=$?
    if test $status -ne 0; then
        echo "ERROR: run_tests() FAILED!. status: $status" >&2
        return $status
    fi

    # Push the dmr++ to S3 target (conditional)
    if test -n "${s3_granule}" && test -n "${S3_UPLOAD}"; then
        if test -n "${VERBOSE}";then
            echo "#######################################################################################" >&2
            echo "# TRANSMITTING RESULTS TO S3" >&2
            echo "#" >&2
        fi
        if test -z "${OUTPUT_FILE}"; then
            echo "!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!" >&2
            echo "ERROR - Unable to upload dmr++ file because the output file name has not been set!" >&2
            echo "        Use the -o parameter. To set the output file name" >&2
            echo "-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -" >&2
            show_usage >&2
            echo "!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!" >&2
            return 1
        fi
        s3_dmrpp="${s3_granule}.dmrpp"
        echo "# Uploading dmr++ file (${OUTPUT_FILE}) to: ${s3_dmrpp}" >&2
        set +e
        aws s3 cp "${OUTPUT_FILE}" "${s3_dmrpp}"
        status=$?
        if test $status -ne 0; then
            echo "ERROR - Failed to upload ${s3_dmrpp} to S3. Exiting..." >&2
            return $status
        fi
        echo "# Checking missing data file: ${s3_local_side_car_missing_file}" >&2
        if test -n "${s3_local_side_car_missing_file}" && test -s "${s3_local_side_car_missing_file}"; then
            local s3_missing_file="${s3_granule}.missing"
            echo "# Uploading missing data file (${s3_local_side_car_missing_file}) to: ${s3_missing_file}" >&2
            aws s3 cp "${s3_local_side_car_missing_file}" "${s3_missing_file}"
            status=$?
            if test $status != 0; then
                echo "ERROR - Failed to upload ${s3_missing_file} to S3. Exiting..." >&2
                return $status
            fi
        else
            echo "# No usable missing data file: ${s3_local_side_car_missing_file}" >&2
            echo "# "$(ls -l "${s3_local_side_car_missing_file}") >&2
        fi
        set -e
    fi
    if test -n "${VERBOSE}"; then
        echo "#######################################################################################" >&2
        if test -n "${MERGE_MISSING_VARS}"; then
            echo "#  SIDECAR_MISSING_FILE: ${SIDECAR_MISSING_FILE}" >&2
        fi
        echo "# END main()" >&2
        echo "#######################################################################################" >&2
    fi
    return 0
}

main
status=$?
if test -n "${VERBOSE}"; then
    echo "# $0" >&2
    echo "# Exiting with status: $status " >&2
    echo "#######################################################################################" >&2
fi
exit $status
