set -e
if [ -e "$NIX_ATTRS_SH_FILE" ]; then source "$NIX_ATTRS_SH_FILE"; fi

export IN_NIX_SHELL=impure
export dontAddDisableDepTrack=1

if [[ -n $stdenv ]]; then
    source $stdenv/setup
fi

# Better to use compgen, but stdenv bash doesn't have it.
__vars="$(declare -p)"
__functions="$(declare -F)"

# Literal control characters (ASCII 0-31) aren't valid JSON.
__escapeCtrl() {
    local escaped="$1"

    # I don't know if NUL bytes are at ALL possible in here,
    # but covering them is free.
    local i=0
    # NOTE: safe input `i` to arithmetic expansion.
    while [[ "$i" -le 32 ]]; do
        # Convert the decimal ASCII value to its actual string.
        local asHex; printf -v asHex "%02x" "$i"
        local asStr; printf -v asStr "%b" "\x$asHex"

        # Format it to \uXXXX.
        # All control characters fit within four hex digits.
        local asUni; printf -v asUni '\\u%04x' "$i"

        escaped="${escaped//"$asStr"/"$asUni"}"

        i="$((i + 1))"
    done

    printf "%s" "$escaped"
}

__dumpEnv() {
    printf '{\n'

    printf '  "bashFunctions": {\n'
    local __first=1
    while read __line; do
        if ! [[ $__line =~ ^declare\ -f\ (.*) ]]; then continue; fi
        __fun_name="${BASH_REMATCH[1]}"
        __fun_body="$(type $__fun_name)"
        if [[ $__fun_body =~ \{(.*)\} ]]; then
            if [[ -z $__first ]]; then printf ',\n'; else __first=; fi
            __fun_body="${BASH_REMATCH[1]}"
            printf "    "
            __escapeString "$__fun_name"
            printf ':'
            __escapeString "$__fun_body"
        else
            printf "Cannot parse definition of function '%s'.\n" "$__fun_name" >&2
            return 1
        fi
    done < <(printf "%s\n" "$__functions")
    printf '\n  },\n'

    printf '  "variables": {\n'
    local __first=1
    while read __line; do
        if ! [[ $__line =~ ^declare\ (-[^ ])\ ([^=]*) ]]; then continue; fi
        local type="${BASH_REMATCH[1]}"
        local __var_name="${BASH_REMATCH[2]}"

        if [[ $__var_name =~ ^BASH_ || \
              $__var_name =~ ^COMP_ || \
              $__var_name = _ || \
              $__var_name = DIRSTACK || \
              $__var_name = EUID || \
              $__var_name = FUNCNAME || \
              $__var_name = HISTCMD || \
              $__var_name = HOSTNAME || \
              $__var_name = GROUPS || \
              $__var_name = PIPESTATUS || \
              $__var_name = PWD || \
              $__var_name = RANDOM || \
              $__var_name = SHLVL || \
              $__var_name = SECONDS || \
              $__var_name = EPOCHREALTIME || \
              $__var_name = EPOCHSECONDS \
            ]]; then continue; fi

        if [[ -z $__first ]]; then printf ',\n'; else __first=; fi

        printf "    "
        __escapeString "$__var_name"
        printf ': {'

        # FIXME: handle -i, -r, -n.
        if [[ $type == -x ]]; then
            printf '"type": "exported", "value": '
            __escapeString "${!__var_name}"
        elif [[ $type == -- ]]; then
            printf '"type": "var", "value": '
            __escapeString "${!__var_name}"
        elif [[ $type == -a ]]; then
            printf '"type": "array", "value": ['
            local __first2=1
            __var_name="$__var_name[@]"
            for __i in "${!__var_name}"; do
                if [[ -z $__first2 ]]; then printf ', '; else __first2=; fi
                __escapeString "$__i"
                printf ' '
            done
            printf ']'
        elif [[ $type == -A ]]; then
            printf '"type": "associative", "value": {\n'
            local __first2=1
            declare -n __var_name2="$__var_name"
            for __i in "${!__var_name2[@]}"; do
                if [[ -z $__first2 ]]; then printf ',\n'; else __first2=; fi
                printf "      "
                __escapeString "$__i"
                printf ": "
                __escapeString "${__var_name2[$__i]}"
            done
            printf '\n    }'
        else
            printf '"type": "unknown"'
        fi

        printf "}"
    done < <(printf "%s\n" "$__vars")
    printf '\n  }'

    if [ -e "$NIX_ATTRS_SH_FILE" ]; then
        printf ',\n  "structuredAttrs": {\n    '
        __escapeString ".attrs.sh"
        printf ': '
        __escapeString "$(<"$NIX_ATTRS_SH_FILE")"
        printf ',\n    '
        __escapeString ".attrs.json"
        printf ': '
        __escapeString "$(<"$NIX_ATTRS_JSON_FILE")"
        printf '\n  }'
    fi

    printf '\n}'
}

__escapeString() {
    local __s="$1"
    __s="${__s//\\/\\\\}"
    __s="${__s//\"/\\\"}"
    __s="${__s//$'\n'/\\n}"
    __s="${__s//$'\r'/\\r}"
    __s="${__s//$'\t'/\\t}"
    __s="$(__escapeCtrl "$__s")"
    printf '"%s"' "$__s"
}

# In case of `__structuredAttrs = true;` the list of outputs is an associative
# array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist`
# must contain the array's keys (hence `${!...[@]}`) in this case.
if [ -e "$NIX_ATTRS_SH_FILE" ]; then
    __olist="${!outputs[@]}"
else
    __olist=$outputs
fi

for __output in $__olist; do
    if [[ -z $__done ]]; then
        __dumpEnv > ${!__output}
        __done=1
    else
        echo -n >> "${!__output}"
    fi
done
