#!/usr/bin/env bash
# Source this file in Bash, or run: bash FILE auto COMMAND [ARG ...]
# Does not use eval or change the caller's shell options.

maximo_normalize_args() {
    MAXIMO_NORMALIZED_ARGS=()
    if (( $# == 0 )); then
        printf '%s\n' 'Expected a space-separated list of value-taking options.' >&2
        return 2
    fi

    local specification=$1 option argument matched
    local -a value_options=()
    shift
    if [[ $specification == *$'\n'* || $specification == *$'\r'* ]]; then
        printf '%s\n' 'Option specifications must fit on one line.' >&2
        return 2
    fi
    if [[ $specification != auto ]]; then
        IFS=$' \t' read -r -a value_options <<< "$specification"
    fi
    for option in "${value_options[@]}"; do
        if [[ ! $option =~ ^-[[:alnum:]][[:alnum:]-]*$ ]]; then
            printf 'Invalid value-taking option: %s\n' "$option" >&2
            return 2
        fi
    done

    while (( $# )); do
        argument=$1
        shift
        if [[ $argument == -- ]]; then
            MAXIMO_NORMALIZED_ARGS+=("--" "$@")
            return 0
        fi

        # Without a tool schema, this is deliberately a heuristic. In particular,
        # a boolean followed by a positional value cannot be distinguished here.
        if [[ $specification == auto ]]; then
            if [[ $argument =~ ^-[[:alpha:]][[:alnum:]-]*$ ]] &&
                (( $# > 0 )) && [[ -n $1 && $1 != -* ]]; then
                MAXIMO_NORMALIZED_ARGS+=("$argument$1")
                shift
            else
                MAXIMO_NORMALIZED_ARGS+=("$argument")
            fi
            continue
        fi

        matched=false
        for option in "${value_options[@]}"; do
            if [[ $argument == "$option" ]]; then
                matched=true
                break
            fi
        done

        if [[ $matched == true ]]; then
            if (( $# == 0 )) || [[ -z $1 || $1 == -* ]]; then
                printf 'Option %s needs a non-empty value; attach values beginning with - directly to the option.\n' "$argument" >&2
                MAXIMO_NORMALIZED_ARGS=()
                return 2
            fi
            MAXIMO_NORMALIZED_ARGS+=("$argument$1")
            shift
        else
            MAXIMO_NORMALIZED_ARGS+=("$argument")
        fi
    done
    return 0
}

# Sourcing only defines the function; it does not process the caller's arguments.
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
    if (( $# < 2 )); then
        printf "Usage: bash %s 'auto | VALUE OPTIONS' COMMAND [ARG ...]\n" "$0" >&2
        exit 2
    fi
    maximo_options=$1
    maximo_command=$2
    shift 2
    maximo_normalize_args "$maximo_options" "$@" || exit "$?"
    exec -- "$maximo_command" "${MAXIMO_NORMALIZED_ARGS[@]}"
fi
