#!/usr/bin/env zsh
# Description:
#   Update installed packages in parallel

trap '__zplug::job::spinner::unlock; trap - SIGINT' SIGINT

local     repo arg f_pkg c_pkg filter
local -aU repos
local -A  tags
local -a  failed_packages
local -A  checked_packages
local -i  max=0
local -F  SECONDS=0 start_time finish_time
local     is_select=false is_force=false

while (( $# > 0 ))
do
    arg="$1"
    case "$arg" in
        --self)
            __zplug::core::self::update \
                ${2:+"$argv[2,-1]"}
            return $status
            ;;
        --select)
            is_select=true
            ;;
        --force)
            is_force=true
            ;;
        -*|--*)
            __zplug::core::options::unknown "$arg"
            return $status
            ;;
        "")
            # Invalid
            return 1
            ;;
        */*)
            repos+=( "${arg:gs:@::}" )
            ;;
        *)
            return 1
            ;;
    esac
    shift
done

# Initialize
{
    if $is_select; then
        __zplug::utils::shell::search_commands \
            "$ZPLUG_FILTER" \
            | read filter
        if [[ -z $filter ]]; then
            __zplug::io::print::f \
                --die \
                --zplug \
                --error \
                --func \
                "There is no available filter in ZPLUG_FILTER\n"
            return 1
        fi
        repos=( ${(@f)"$(echo "${(Fk)zplugs[@]}" | eval "$filter")"} )

        # Cace of type Ctrl-C
        if (( $#repos == 0 )); then
            return 0
        fi
    fi

    if (( $#repos == 0 )); then
        repos=( "${(k)zplugs[@]:gs:@::}" )
    fi

    for repo in "${repos[@]}"
    do
        (( $#repo > $max )) && max=$#repo
    done
}

__zplug::job::spinner::lock
__zplug::job::spinner::spin &

for repo in "${repos[@]}"
do
    if ! __zplug::base::base::zpluged "$repo"; then
        __zplug::job::spinner::unlock
        __zplug::io::print::f \
            --die \
            --zplug \
            --func \
            "$repo: no such package\n"
        return 1
    fi

    # Run installation in subprocess
    {
        # All variables are treated as local variable
        # because of background job (subprocess)
        local    key
        local -i ret=1
        local -A tags

        __zplug::core::tags::parse "$repo"
        tags=( "${reply[@]}" )

        # Run in anonymous function for using its exit code
        function () {
            __zplug::job::spinner::echo "%-20s %s\n" \
                "Updating..." \
                "$repo"

            if $is_force; then
                # DO update anyway
                :
            else
                # if frozen:yes is given
                if (( $_zplug_boolean_true[(I)$tags[frozen]] )); then
                    return $_ZPLUG_STATUS_REPO_FROZEN
                fi
            fi

            if __zplug::core::sources::is_handler_defined "update" "$tags[from]"; then
                __zplug::core::sources::use_handler \
                    "update" \
                    "$tags[from]" \
                    "$repo"
                return $status
            fi
        }
        ret=$status

        case "$ret" in
            $_ZPLUG_STATUS_SUCCESS)
                __zplug::job::spinner::echo "$fg[green]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    "Updated!" \
                    "$repo" \
                    $SECONDS

                # hook after updating
                __zplug::job::hook::build "$repo"
                ;;
            $_ZPLUG_STATUS_FAILURE)
                __zplug::job::spinner::echo "$fg[red]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    --die \
                    "Failed to update" \
                    "$repo" \
                    $SECONDS
                ;;
            $_ZPLUG_STATUS_REPO_NOT_FOUND)
                sleep 1
                __zplug::job::spinner::echo "$fg[magenta]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    --die \
                    "Repo not found" \
                    "$repo" \
                    $(( SECONDS - 1 ))
                ;;
            $_ZPLUG_STATUS_REPO_FROZEN)
                sleep 1
                __zplug::job::spinner::echo "$fg[blue]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    --die \
                    "Frozen repo" \
                    "$repo" \
                    $(( SECONDS - 1 ))
                ;;
            $_ZPLUG_STATUS_REPO_UP_TO_DATE)
                __zplug::job::spinner::echo "$fg[white]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    --die \
                    "Up-to-date" \
                    "$repo" \
                    $SECONDS
                ;;
            $_ZPLUG_STATUS_REPO_LOCAL)
                sleep 1
                __zplug::job::spinner::echo "$fg[yellow]%-20s$reset_color %-${max}s\t(%.2fs)\n" \
                    --die \
                    "Skipped due to local repo" \
                    "$repo" \
                    $(( SECONDS - 1 ))
                ;;
        esac
    } &
    __zplug::job::queue::enqueue "$!"
    __zplug::job::queue::wait
done

__zplug::job::queue::wait_all
__zplug::job::spinner::unlock

return $ret
