#! /bin/bash
# vim: shiftwidth=4 tabstop=4 expandtab

# Retrieve bash completion variables
COMP_CWORD=${COMMAND_ARGS[0]}
COMP_WORDS=( "${COMMAND_ARGS[@]:1}" )
cur=${COMP_WORDS[COMP_CWORD]}

# Reset command related variables
COMMAND=""
COMMAND_ARGS=()
COMMAND_ARGS_CWORD=""
COMMAND_COMP=0
# shellcheck disable=SC2034
IN_AUTOCOMPLETER=1

# Set default options
OPTS=(
    -A --auto -F --force -v --verbose -d --debug -x --trace -L --log-level -C --console -q --quiet
    -j --just-try -X --generate-script -h --help help -N --no-cache
)


function comp_found() {
    [[ -n "$1" ]] && echo "$1" || echo "${OPTS[@]}"
    exit 0
}

function comp_vms() {
    mapfile -t -O "${#OPTS[@]}" OPTS < <( $0 ls -a | awk '{print $3}' | sort -u | grep -E "^$cur" )
}

function comp_running_vms() {
    mapfile -t -O "${#OPTS[@]}" OPTS < <( $0 ls | awk '{print $3}' | sort -u | grep -E "^$cur" )
}

function comp_hyps() {
    mapfile -t -O "${#OPTS[@]}" OPTS < <( tr ' ' '\n' <<< "${HYPS:-}" )
    [[ $# -gt 1 ]] && mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- "${@:1}" )
}

function comp_int() {
    mapfile -t OPTS < <( seq 0 9 | sed "s/^/$1/" )
}

function comp_filedir() {
    # Exit with a custom code handled by our vm completion handler
    exit 64
}

function comp_another_command() {
    # Complete another command using _command_offset bash-completion helpers
    # The another command must be specified after the "--" argument. If this argument is not
    # already provided, always return it as the unique option. Otherwise, we need to provide
    # an the offset to _command_offset and for that, we identify the index of the "--" argument
    # and exit with [200 + index + 1]. All exit codes greater than 200 will be pass to
    # _command_offset with offset=(($? - 200)).
    OPTS=( "--" )
    idx=1
    while [[ $idx -lt $COMP_CWORD ]]; do
        [[ "${COMP_WORDS[$idx]}" == "--" ]] && exit "$((idx+201))"
        ((idx++))
    done
    comp_found
}

function comp_rbd_images() {
    if [[ $(echo "$cur"|grep -Ec "/") -eq 0 ]]; then
        # Complete with volume pools names
        list_volume_pools POOLS
        OPTS+=( "${POOLS[@]/%/\/}" )
    else
        # Complete with pool's volumes names
        local pool
        pool=$( cut -d'/' -f1 <<< "$cur")
        mapfile -t -O "${#OPTS[@]}" OPTS < <( list_pool_volumes "$pool" | sed "s#^#$pool/#" )
    fi
}

idx=1
while [ $idx -le ${#COMP_WORDS[@]} ]; do
    OPT=${COMP_WORDS[$idx]}
    case $OPT in
        -A|--auto)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -A --auto )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -F|--force)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -F --force )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -v|--verbose)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -v --verbose )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -d|--debug)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -d --debug )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -x|--trace)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -x --trace )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -L|--log-level)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -L --log-level )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
            [[ $((idx+1)) -eq $COMP_CWORD ]] && OPTS=( "${!LOG_LEVELS[@]}" ) && comp_found
        ;;
        -C|--console)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -C --console )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -q|--quiet)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -q --quiet )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -j|--just-try)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -j --just-try )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -h|--help|help)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -h --help help )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        -X|--generate-script)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -X --generate-script )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
            ((idx++))
            [[ $idx -eq $COMP_CWORD ]] && comp_filedir "${!idx}"
        ;;
        -N|--no-cache)
            mapfile -t -O "${#OPTS[@]}" OPTS < <( array_filter "${OPTS[@]}" -- -N --no-cache )
            [[ $idx -eq $COMP_CWORD ]] && comp_found "$OPT"
        ;;
        *)
            if [[ -z "$COMMAND" ]]; then
                COMMAND="$OPT"
                if [[ $idx -eq $COMP_CWORD ]]; then COMMAND_COMP=1; else check_command; fi
            else
                COMMAND_ARGS+=("$OPT")
                [[ $idx -eq $COMP_CWORD ]] && (( COMMAND_ARGS_CWORD=${#COMMAND_ARGS[@]}-1 ))
            fi
    esac
    ((idx++))
done

if [[ -z "$COMMAND" ]] || [[ $COMMAND_COMP -eq 1 ]]; then
    list_available_commands
    OPTS+=( "${AVAILABLE_COMMANDS[@]}" )
elif [[ -n "$COMMAND_ARGS_CWORD" ]]; then
    # Complete command argument
    case "$COMMAND" in
        start|suspend|resume|stop|reboot|reset|destroy|info|dumpxml|edit|clone|unlock|shell|console|backup)
            [[ $COMMAND_ARGS_CWORD -eq 0 ]] && comp_vms
            ;;

        agent)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=( check info )
            else
                subcommand="${COMMAND_ARGS[0]}"
                case $subcommand in
                    check)
                        OPTS+=( ALL )
                        comp_running_vms
                        ;;
                    info)
                        OPTS+=( -J --json )
                        comp_running_vms
                        ;;
                esac
            fi
            ;;

        cluster)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=( stats check_allocated_resources cpuinfo sync copy exec auto-upgrade backup )
            else
                subcommand="${COMMAND_ARGS[0]}"
                case $subcommand in
                    stats)
                        # shellcheck source=/dev/null
                        . "$LIB_DIR/stats"
                        OPTS+=( --json "${HYP_STATS_STATUS[@]}" )
                        ;;
                    check_allocated_resources)
                        check_args=(
                            -r --hyp-reserved-memory
                            -w --warning-cpu-threshold -c --critical-cpu-threshold
                            -W --warning-memory-threshold -C --critical-memory-threshold
                        )
                        previous_arg="${COMMAND_ARGS[$((COMMAND_ARGS_CWORD-1))]}"
                        if in_array "$previous_arg" "${check_args[@]}"; then
                            comp_int "${COMMAND_ARGS[$COMMAND_ARGS_CWORD]}"
                        else
                            # shellcheck source=/dev/null
                            . "$LIB_DIR/stats"
                            OPTS+=( "${HYP_STATS_STATUS[@]}" "${check_args[@]}" )
                            comp_hyps
                        fi
                        ;;
                    sync)
                        # shellcheck source=/dev/null
                        . "$LIB_DIR/commands/cluster"
                        OPTS+=( -a --all -i --init "${CLUSTER_SYNC_FILES_AND_DIRS[@]}" )
                        ;;
                    copy)
                        comp_filedir
                        ;;
                    exec)
                        comp_another_command
                        ;;
                    backup)
                        if [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                            OPTS+=( all active inactive check )
                        elif [[ $COMMAND_ARGS_CWORD -gt 1 ]]; then
                            if [[ "${COMMAND_ARGS[1]}" == "check" ]]; then
                                check_opts=(-w --warning-age -w --critical-age)
                                previous_arg="${COMMAND_ARGS[$((COMMAND_ARGS_CWORD-1))]}"
                                if in_array "$previous_arg" "${check_opts[@]}"; then
                                    comp_int "${COMMAND_ARGS[$COMMAND_ARGS_CWORD]}"
                                else
                                    OPTS+=( "${check_opts[@]}" )
                                fi
                            else
                                comp_hyps "${COMMAND_ARGS[@]:2}"
                            fi
                        fi
                        ;;
                esac
            fi
            ;;

        hypervisor)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=( stats candidates check_allocated_resources )
            else
                # shellcheck source=/dev/null
                . "$LIB_DIR/stats"
                subcommand="${COMMAND_ARGS[0]}"
                case $subcommand in
                    stats)
                        OPTS+=( --json "${HYP_STATS_STATUS[@]}" )
                        comp_hyps
                        ;;
                    candidates)
                        if [[ $COMMAND_ARGS_CWORD -eq 1 ]] || [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                            comp_int
                        elif ! in_array --best "${COMMAND_ARGS[@]}"; then
                            OPTS+=( --best )
                        fi
                        ;;
                    check_allocated_resources)
                        check_args=( -r --hyp-reserved-memory -w --warning-cpu-threshold -c --critical-cpu-threshold )
                        previous_arg="${COMMAND_ARGS[$((COMMAND_ARGS_CWORD-1))]}"
                        if in_array "$previous_arg" "${check_args[@]}"; then
                            comp_int "${COMMAND_ARGS[$COMMAND_ARGS_CWORD]}"
                        else
                            OPTS+=( "${HYP_STATS_STATUS[@]}" "${check_args[@]}" )
                            comp_hyps
                        fi
                esac
            fi
            ;;

        locate)
            idx=0
            VM=""
            while [ $idx -le ${#COMMAND_ARGS[@]} ]; do
                OPT=${COMMAND_ARGS[$idx]}
                case $OPT in
                    -a|--all|-p|--paused)
                        [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                        ADD_OPTS=0
                        break
                        ;;
                    *)
                        if [[ -z "$VM" ]]; then
                            [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_vms
                            VM="$OPT"
                        fi
                        ;;
                esac
                ((idx++))
            done
            [[ $ADD_OPTS -eq 1 ]] && OPTS+=( -a --all -p --paused )
            ;;

        lock)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=( ls add rm )
            elif [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                comp_vms
                subcommand="${COMMAND_ARGS[0]}"
                [[ "$subcommand" == "ls" ]] && OPTS+=( -J --json )
            elif [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                subcommand="${COMMAND_ARGS[0]}"
                if [[ "$subcommand" == "ls" ]]; then
                    OPTS+=( -J --json )
                elif [[ "$subcommand" == "rm" ]]; then
                    HYP=$( locate "${COMMAND_ARGS[1]}" )
                    check_hyp "$HYP" && mapfile -t -O "${#OPTS[@]}" OPTS < <(
                        list_locks "${COMMAND_ARGS[1]}" "$HYP" | jq -rc '.[] | .id' | sort -u
                    )
                fi
            fi
            ;;

        ls)
            # shellcheck source=/dev/null
            . "$LIB_DIR/commands/ls"
            OPTS+=(
                "${VIRSH_SUPPORTED_ARGS[@]}"
                "${VIRSH_SHORT_ANSWER_SUPPORTED_ARGS[@]}"
                "${!VIRSH_SUPPORTED_ARGS_ALIASES[@]}"
            )
            ;;

        migrate)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                comp_vms
            elif [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                mapfile -t -O "${#OPTS[@]}" OPTS < <(
                    tr ' ' '\n' <<< "${HYPS:-}" | grep -Ev "^$(hostname)$"
                )
            elif [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                OPTS+=( stop-before )
            fi
            ;;

        pid)
            if in_array -r "${COMMAND_ARGS[0]}" || in_array --reverse "${COMMAND_ARGS[0]}"; then
                mapfile -t -O "${#OPTS[@]}" OPTS < <( pgrep -f '(qemu-system|/usr/bin/kvm)' )
            else
                OPTS+=( -r --reverse )
                mapfile -t -O "${#OPTS[@]}" OPTS < <( list_vms )
            fi
            ;;

        restore)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                # shellcheck source=/dev/null
                . "$LIB_DIR/backup"
                _backup_dir BACKUP_DIR
                mapfile -t BACKUP_VMS < <(
                    find "$BACKUP_DIR" -type f -name "*.xml" -printf "%f\n" | sed 's/\.xml$//'
                )
                OPTS+=( "${BACKUP_VMS[@]}" )
            elif [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                comp_hyps
            fi
            ;;

        snapshot)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=( create info ls protect unprotect size rm restore scheduled )
            elif [[ $COMMAND_ARGS_CWORD -eq 1 ]] && \
                ! in_array "${COMMAND_ARGS[0]}" create ls scheduled ; then
                comp_vms
            elif [[ $COMMAND_ARGS_CWORD -eq 2 ]] && \
                in_array "${COMMAND_ARGS[0]}" info protect unprotect size rm restore ; then
                # Autocomplete with VM snap names
                mapfile -t -O "${#OPTS[@]}" OPTS < <(
                    $0 snapshot ls "${COMMAND_ARGS[1]}" 2>/dev/null
                )
            else
                # shellcheck source=/dev/null
                . "$LIB_DIR/commands/snapshot"
                subcommand="${COMMAND_ARGS[0]}"
                previous_arg="${COMMAND_ARGS[$((COMMAND_ARGS_CWORD-1))]}"
                case "$subcommand" in
                    create)
                        OPTS+=( -p --prefix -s --suffix -P --volumes-pattern )
                        idx=1
                        while [ $idx -le ${#COMMAND_ARGS[@]} ]; do
                            OPT=${COMMAND_ARGS[$((idx))]}
                            case $OPT in
                                -p|--prefix)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -p --prefix
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    ((idx++))
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && OPTS=( ) && comp_found
                                    ;;
                                -s|--suffix)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -s --suffix
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    ((idx++))
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && OPTS=( ) && comp_found
                                    ;;
                                -P|--volumes-pattern)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -P --volumes-pattern
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    ((idx++))
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && OPTS=( ) && comp_found
                                    ;;
                                *)
                                    [[ -z "$VM" ]] && [[ $idx -ne $COMMAND_ARGS_CWORD ]] && VM=$OPT
                                    ;;
                            esac
                            ((idx++))
                        done
                        [[ -z "$VM" ]] && comp_vms
                        ;;
                    ls)
                        VM=""
                        OPTS+=( -l --details --full -J --json )
                        idx=1
                        while [ $idx -le ${#COMMAND_ARGS[@]} ]; do
                            OPT=${COMMAND_ARGS[$((idx))]}
                            case $OPT in
                                -l|--details|--full)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -l --details --full
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    ;;
                                -J|--json)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -J --json
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    ;;
                                *)
                                    [[ -z "$VM" ]] && [[ $idx -ne $COMMAND_ARGS_CWORD ]] && VM=$OPT
                                    ;;
                            esac
                            ((idx++))
                        done
                        [[ -z "$VM" ]] && comp_vms
                        ;;
                    rm)
                        [[ $COMMAND_ARGS_CWORD -eq 3 ]] && OPTS+=( wait )
                        ;;
                    scheduled)
                        if in_array "$previous_arg" -f --format -p --pattern; then
                            OPTS=( ); comp_found
                        elif in_array "$previous_arg" -D --weekday; then
                            OPTS=( 1 2 3 4 5 6 7 ); comp_found
                        fi
                        OPTS+=( -f --format -p --pattern )
                        scheduled_subcommand=
                        idx=1
                        while [ $idx -le ${#COMMAND_ARGS[@]} ]; do
                            OPT=${COMMAND_ARGS[$((idx))]}
                            case $OPT in
                                -f|--format)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -f --format
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                ;;
                                -p|--pattern)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -p --pattern
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                ;;
                                -D|--weekday)
                                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                                        array_filter "${OPTS[@]}" -- -D --weekday
                                    )
                                    [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                ;;
                                *)
                                    if [[ -z "$scheduled_subcommand" ]] && \
                                        in_array "$OPT" "${SCHEDULED_SUBCOMMANDS[@]}"; then
                                        scheduled_subcommand="$OPT"
                                        [[ $idx -eq $COMMAND_ARGS_CWORD ]] && comp_found "$OPT"
                                    else
                                        case "$scheduled_subcommand" in
                                            list)
                                                comp_rbd_images
                                                ;;
                                            run)
                                                OPTS+=( -D --weekday )
                                                get_scheduled_snapshots_vms_list
                                                OPTS+=( "${SCHEDULED_SNAPSHOTS_VMS_LIST[@]}" )
                                                ;;
                                            cleanup)
                                                [[ ! "${COMMAND_ARGS[*]}" =~ .+/.* ]] && \
                                                    OPTS+=( ALL )
                                                comp_rbd_images
                                                ;;
                                            check)
                                                check_opts=(
                                                    -W --warning-age -C --critical-age
                                                    -w --warning-count -c --critical-count
                                                )
                                                if in_array "$previous_arg" "${check_opts[@]}"; then
                                                    comp_int "${COMMAND_ARGS[$COMMAND_ARGS_CWORD]}"
                                                else
                                                    OPTS+=( "${check_opts[@]}" )
                                                fi
                                                ;;
                                        esac
                                    fi
                            esac
                            ((idx++))
                        done
                        [[ -z "$scheduled_subcommand" ]] && OPTS+=( "${SCHEDULED_SUBCOMMANDS[@]}" )
                        ;;
                esac
            fi
            ;;

        state)
            [[ $COMMAND_ARGS_CWORD -eq 0 ]] && comp_vms
            if [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                # shellcheck source=/dev/null
                . "$LIB_DIR/commands/state"
                OPTS+=( "${POSSIBLE_EXPECTED_STATES[@]}" )
            fi
            ;;

        volume)
            if [[ $COMMAND_ARGS_CWORD -eq 0 ]]; then
                OPTS+=(
                    ls info owner add attach resize children flatten map lsmap unmap detach rm
                    locate
                )
            else
                subcommand=${COMMAND_ARGS[0]}
                if in_array "$subcommand" ls add attach resize children flatten map unmap detach && \
                   [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                    comp_vms
                elif in_array "$subcommand" info owner rm && [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                    comp_rbd_images
                elif [[ $subcommand == "add" ]] && [[ $COMMAND_ARGS_CWORD -eq 4 ]]; then
                    mapfile -t -O "${#OPTS[@]}" OPTS < <( list_pool_volumes )
                elif [[ "$subcommand" == "attach" ]] && [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                    comp_rbd_images
                elif in_array "$subcommand" resize children flatten map unmap detach && \
                     [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                    VM=${COMMAND_ARGS[1]}
                    HYP=$( locate "$VM" )
                    check_hyp "$HYP" && mapfile -t -O "${#OPTS[@]}" OPTS < <(
                        list_volumes "$VM" "$HYP" | grep -E "^$cur"
                    )
                elif [[ $subcommand == "locate" ]] && [[ $COMMAND_ARGS_CWORD -eq 1 ]]; then
                    list_volume_pools POOLS
                    array_grep POOLS -E "^$cur"
                    OPTS+=( "${POOLS[@]}" )
                elif [[ $subcommand == "locate" ]] && [[ $COMMAND_ARGS_CWORD -eq 2 ]]; then
                    mapfile -t -O "${#OPTS[@]}" OPTS < <(
                        list_pool_volumes "${COMMAND_ARGS[1]}" | grep -E "^$cur"
                    )
                elif in_array "$subcommand" add resize && \
                    [[ $COMMAND_ARGS_CWORD -eq 3 ]]; then
                    comp_int "${COMMAND_ARGS[$COMMAND_ARGS_CWORD]}"
                fi
            fi
            ;;

    esac
fi

implode $'\n' "${OPTS[@]}" | sed -e 's/\(.*\s.*\)/"\1"/g' -e 's/\(\s\)/\\\1/g'
