#!/bin/bash
#
# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2019 NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
CATEGORIES=('control' 'con' 'information' 'info' 'all' 'task' \
            'admin' 'prep' 'preparation' 'discovery' 'utility' \
            'util')
                
HELP_OPTS=('help' '--help' '-h' 'h' '?')

print_version() {
    VN=$(python3 -c "from cylc.flow import __version__; print(__version__)")
    if [[ "$#" -eq 0 ]]; then
        echo "$VN"
    fi
    if [[ "$*" == 'long' || "$*" == '--long' ]]; then
	LOC1=$(dirname "$0")
	LOC2=$(cd "${LOC1}"; pwd -P)
	echo  "${VN}(${LOC2})"
    fi
}

init_cylc() {
    set -eu
    CYLC_HOME_BIN=$(cd "$(dirname "$0")" && pwd -P)
    PYTHONUNBUFFERED='true'
    export PYTHONUNBUFFERED
}

help_util() {
    # Deals with form 'cylc [COMMAND] --help'
    cd "${CYLC_HOME_BIN}"
    # For help command/option with no args
    if [[ $# == 0 && "${HELP_OPTS[*]} " == *"$UTIL"* ]]; then
        exec "${CYLC_HOME_BIN}/cylc-help"
    fi
    # for cases 'cylc --help CATEGORY COMMAND'
    if [[ $# -gt 1 &&  "${HELP_OPTS[*]} " == *"$UTIL"* && \
          " ${CATEGORIES[*]} " == *" $1 "* ]]; then
        UTIL="$(get_command_from_abbr "$2")"
        run_cylc_command '--help'
    fi
    # Check if this is a help command, not containing a category qualifier
    if [[ $# -gt 1  && "${HELP_OPTS[*]} " == *"${UTIL}"* && 
          " ${CATEGORIES[*]} " != *" $1 "* &&
          " ${CATEGORIES[*]} " != *" $2 "* ]]; then
        run_cylc_command '--help'
    fi
    # If category name is used, call the help func with the category
    # make this deal with 'cylc help CATEGORY' only
    if [[ " ${CATEGORIES[*]} " == *" ${UTIL} "* \
          || " ${HELP_OPTS[*]} " == *" ${UTIL} "* \
          && $# -gt 0 && ( " ${CATEGORIES[*]} " == *" $1 "* \
          || " ${HELP_OPTS[*]} " == *" $1 "* ) ]]; then
        UTIL='help'
        run_cylc_command "$@"
    fi
    # Deal with cases like 'cylc --help [COMMAND/CATEGORY] or cylc [CATEGORY]'   
    if (( $# >= 1 )); then
        if [[ "$1" == 'version' ]]; then
            cat <<'__HELP__'
Usage: cylc version [--long]

Print version and exit.

Options:
  --long            print cylc version and location of cylc installation
__HELP__
            return
        else
            UTIL="$(get_command_from_abbr "$1")"
            run_cylc_command '--help'
        fi
    fi
    # If category name is used as arg, call the help func with the category
    local COMMAND
    if [[ $# -gt 1  &&  ( "${CATEGORIES[*]} " == *" $1 "* 
            || " ${HELP_OPTS[*]} " == *" $1 "* ) ]]; then
        COMMAND="${CYLC_HOME_BIN}/cylc-help"
        exec "${COMMAND}" "$@"
    fi
    # Deal with cases like 'cylc --help [COMMAND/CATEGORY]'
    if [[ $# -gt 1 && -f "$(ls "cylc-$2"* 2>'/dev/null')" ]]; then
        COMMAND="${CYLC_HOME_BIN}/$(ls "cylc-$2"*)"
        exec "${COMMAND}" "--help"
    fi
    # If not a category or not an actual command in the bin dir, exit
    if ! ls "cylc-${UTIL}"* 2>'/dev/null'; then
        echo "${UTIL}: unknown utility. Abort." >&2
        echo "Type 'cylc help' for a list of utilities."
        return 1
    fi
    
    echo "Something has gone terribly wrong if you are here..."
    return 1 
}

category_help_command() {
# Special case for printing categories
    local COMMAND="${CYLC_HOME_BIN}/cylc-help"
    exec "${COMMAND}" "categories"
}

command_help_command() {
# Special case for printing all commands (used in CUG Makefile)
    local COMMAND="${CYLC_HOME_BIN}/cylc-help"
    exec "${COMMAND}" "commands"
}

path_lead() {
# Ensure that ITEM_STR is at the beginning of PATH_STR
    local PATH_STR="$1"
    local ITEM_STR="$2"
    if [[ -z "${PATH_STR:-}" ]]; then
        echo "${ITEM_STR}"
    elif [[ "${PATH_STR}" != "${ITEM_STR}" \
            && "${PATH_STR}" != "${ITEM_STR}":* ]]; then
        while [[ "${PATH_STR}" == *:"${ITEM_STR}" ]]; do
            PATH_STR=${PATH_STR%:$ITEM_STR}
        done
        while [[ "${PATH_STR}" == *:"${ITEM_STR}":* ]]; do
            local PATH_HEAD="${PATH_STR%:$ITEM_STR:*}"
            local PATH_TAIL="${PATH_STR##*:$ITEM_STR:}"
            PATH_STR="${PATH_HEAD}:${PATH_TAIL}"
        done
        echo "${ITEM_STR}:${PATH_STR}"
    else
        echo "${PATH_STR}"
    fi
}

get_command_from_abbr() {
    local COMMAND
    COMMAND="$(cd "${CYLC_HOME_BIN}" && ls "cylc-$1"* 2>'/dev/null')"
    if [[ -z "${COMMAND}" ]]; then
        # Abbreviation has no match, bad
        echo "cylc $1: unknown utility. Abort." >&2
        echo "Type \"cylc help all\" for a list of utilities." >&2
    elif (("$(wc -l <<<"${COMMAND}")" != 1)); then
        # Abbreviation has multiple matches, bad
        echo "cylc $1: is ambiguous for:" >&2
        sed 's/^cylc-/    cylc /' <<<"${COMMAND}" >&2
    else
        # Abbreviation has one match, good
        sed 's/^cylc-//' <<<"${COMMAND}"
        return
    fi
    return 1
}

run_cylc_command() {
    exec "${CYLC_HOME_BIN}/cylc-${UTIL}" "$@"
}

init_cylc

UTIL="help"
if (( $# > 0 )); then
    UTIL="$1"
    shift 1
fi
# For speed, assume a correct command has been provided first of all. 
if [[ -f "${CYLC_HOME_BIN}/cylc-${UTIL}" && "${UTIL}" != 'help'  ]]; then
    run_cylc_command "$@"
fi

# Now process help and command matching.
# Check for help or version options.
case "${UTIL}" in
# Deal with categories
control|information|all|task|admin|preparation|discovery\
        |utility|util|prep|con|info)
    if (( $# == 0 )); then
        help_util "${UTIL}"
    fi
    if [[ $# -ge 1 && "${HELP_OPTS[*]} " == *"$1"* ]]; then
        help_util "${UTIL}"
    fi
    UTIL="$1"
    # Discard the category qualifier argument by shifting
    # the arguments along by 1.
    shift 1
    :;;
help|h|\?|--help|-h)
    help_util "$@" | ${PAGER:-less}
    exit 0
    :;;
version|--version|-V)
    print_version "$@"
    exit 0
    :;;
categories)
    category_help_command "$@"
    exit 0
    :;;
commands)
    command_help_command "$@"
    exit 0
    :;;
esac

# User has not given a help, category, or version option.
# So now deal with matching commands and disambiguation.
UTIL="$(get_command_from_abbr "${UTIL}")"
run_cylc_command "$@"
