#compdef conan

## Necessary configuration to work-around deficiencies:
typeset -a CONAN_CMD
CONAN_CMD=(CONAN_USER_HOME=~/conan-repo/ ~/bin/conan)
# todo:
# extract the settings items.
# in future I can react on $CONAN_USER_HOME even set on the current cmdline.

# extract the settings items.

# see _git in /usr/share/zsh/functions/Completion/Unix/_git
# functions: __git_config_option and
# declare -a sections sections_and_options options
# it writes into the $options

# bug: in 'git -c color.branch.current=dim<TAB>'
#        case $IPREFIX in
          # Note: If you add a branch to this 'case' statement,
          # update $sections_that_permit_arbitrary_subsection_names.
#          (alias.)

# useful commands:

# conan config home
# conan config get

# get the list of packages (in local repo):
# conan search --raw
# conan search --raw  boost
# conan search --raw -rev boost
# test:
# conan get boost/20191111.125432+1.70.0@tomtom/beta

# what I use:
# _arguments
# _describe  ... commands
# _multi_parts
# _sep_parts
# _sequence
# _description (prepares options for compadd)

local LOG_FILE=/run/user/$(id -u)/tab-complete
[[ -e $LOG_FILE ]] || touch $LOG_FILE

# conan search --raw

### the reference: boost/20191111.125432+1.70.0@tomtom/beta
_conan_package_name() {
    # zparseopts -D -E -a opts x: X: J: V: a=all
    local storage_path=$1
    local names=($storage_path/*(/:t))
    # todo: timestamp of the $storage_path controls the cache!
    # _store_cache CACHE_IDENTIFIER PARAM ...

    compstate[insert]=automenu-unambiguous
    compadd -a -S "/"  -q -- names
}

_conan_package_version() {
    local storage_path=$1
    shift
    local package=$1

    local names=($storage_path/$package/*(/:t))
    compadd -a -S "@"  -q -- names
}

_conan_package_version_user() {
    local storage_path=$1
    shift
    local package=$1
    local version=$2

    local names=($storage_path/$package/$version/*(/:t))
#    compstate[insert]=automenu-unambiguous
    compadd -a -S "/"  -q -- names
}

_conan_package_version_user_channel() {
    local storage_path=$1
    shift
    local package=$1
    local version=$2
    local user=$3

    local names=($storage_path/$package/$version/$user/*(/:t))
    compadd -a  -q -- names
}

# remote: conan search --raw
_conan_local_packages() {
    # naive:
    # _path_files -/  -W "$storage_path" -g '*'

    # split the string into 4 parts
    # xxx / vvv @  yyy / zzz

    # transfer Iprefix <- prefix
    local package
    local version
    local user channel
    local storage=$(eval $CONAN_CMD[@] config get storage.path 2>/dev/null)

    if ! compset -P 1 '*/';
    then
        package=$PREFIX
        # if it contains "." then no!
        if [[ $package =~ '^\.' ]]; then
            echo "invalid package name!"  >>$LOG_FILE
            return 1;
        fi
        _conan_package_name $storage
    else
        package=$IPREFIX[0,-2]
        # drop the last '/'
        ((len= $#package + 1 ))
        if ! compset -P 1 '*@';
        then
            version=$PREFIX
            _conan_package_version $storage $package
        else
            # minus ...
            version=$IPREFIX[((len + 1)),-2]
            (( len += $#version + 1 ))

            if ! compset -P 1 '*/'
            then
                user=$PREFIX
                _conan_package_version_user $storage $package $version
            else
                user=$IPREFIX[(( len + 1)),-2]
                (( len += $#user + 1 ))
                channel=$PREFIX
                # compset ?
                _conan_package_version_user_channel $storage $package $version $user
            fi
        fi
    fi
}

_conan_reference()
{
    # this issue here is that I need:
    # pkg/version @ so I need the file, and then '@' and again 2 levels!
    _conan_local_packages

    # _sequence
    # _sep_parts log_items . general_items ":" storage_items
}

_conan_pattern_or_reference()
{
    _conan_reference
}


### Commands:
# plan:

# complete
# 1/ commands
# 2/ single command options
#   dirs, filename
#   profiles
#   pkg names

local consumer_cmds=(
    install:"Installs the requirements specified in a recipe (conanfile.py or conanfile.txt)."
    config:"Manages Conan configuration."
    get:"Gets a file or list a directory of a given reference or package."
    info:"Gets information about the dependency graph of a recipe."
    search:"Searches package recipes and binaries in the local cache or in a remote."
)

local creator_cmds=(
  new:"Creates a new package recipe template with a 'conanfile.py' and optionally, 'test_package' testing files."
  create:"Builds a binary package for a recipe (conanfile.py)."
  upload:"Uploads a recipe and binary packages to a remote."
  export:"Copies the recipe (conanfile.py & associated files) to your local cache."
  export-pkg:"Exports a recipe, then creates a package from local source and build folders."
  test:"Tests a package consuming it from a conanfile.py with a test() method."
)

local package_cmds=(
    source:"Calls your local conanfile.py 'source()' method."
    build:"Calls your local conanfile.py 'build()' method."
    package:"Calls your local conanfile.py 'package()' method."
    editable:"Manages editable packages (package that resides in the user workspace, but are consumed as if they were in the cache)."
    workspace:"Manages a workspace (a set of packages consumed from the user workspace that belongs to the same project)."
)

local misc_cmds=(
  profile:"Lists profiles in the '.conan/profiles' folder, or shows profile details."
  remote:"Manages the remote list and the package recipes associated to a remote."
  user:"Authenticates against a remote with user/pass, caching the auth token."
  imports:"Calls your local conanfile.py or conanfile.txt 'imports' method."
  copy:"Copies conan recipes and packages to another user/channel."
  remove:"Removes packages or binaries matching pattern from local cache or remote."
  alias:"Creates and exports an 'alias package recipe'."
  download:"Downloads recipe and binaries to the local cache, without using settings."
  inspect:"Displays conanfile attributes, like name, version and options. Works locally, in local cache and remote."
  help:"Shows help for a specific command."
  graph:"Generates and manipulates lock files."
)

local cmdtypes=( misc_cmds package_cmds creator_cmds consumer_cmds )
# allcmds=( $misc_cmds $package_cmds $creator_cmds $consumer_cmds )

# obsolete:
_conan_commands()
{
    local -a allcmds

    for cmdtype in $cmdtypes; do
        # todo:
        # local ${cmdtype}_m

        set -A ${cmdtype}_m ${(P)cmdtype%%:*}
        allcmds+=( ${(P)${:-${cmdtype}_m}} )
        #set -A ${cmdtype}_m ${(P)cmdtype%%:*}
        compadd $allcmds
        # "$expl[@]" -a misc_cmds
    done
    # compadd "$expl[@]" -a misc_cmds
    #allcmds
}


# present the commands as block, with documentation:
_conan_commands1()
{
    _describe -t consumer "Consumer commands" consumer_cmds
    _describe -t creator "Creator commands" creator_cmds
    _describe -t misc "Misc commands" misc_cmds
    _describe -t package "Package development commands" package_cmds
}


### common options:
local ENV_OPTIONS=(
    {-e,--env}'[set ENV variables]:variable:_parameters'
    # CC CXX CFLAGS...
    # -e ENV, --env ENV     Environment variables that will be set during the package build, -e CXX=/usr/bin/clang++
    )

PROFILE_OPTIONS=(
    {-pr,--profile}'[profile]:profile:_conan_profile_name'
    #-pr PROFILE, --profile PROFILE
    #                    Apply the specified profile to the install command
    )

local KEEP_SOURCE_OPTIONS=(
    {-k,-ks,--keep-source}'[Do not remove the source folder in local cache, even if the recipe changed]'
)

local OPTIONS_OPTIONS=(
    # Define options values, e.g., -o Pkg:with_qt=true
    {-o,--options}'[options]:option:_conan_options'
    )

local SETTING_OPTIONS=(
    '*'{-s,--settings}'[settings override]: :_conan_setting'
    #       Settings to build the package, overwriting the
    #       defaults. e.g., -s compiler=gcc
)

local REMOTE_OPTIONS=(
    {-r,--remote}'[look in the specified remote server]:conan remote:_conan_remote'
)
#  -r REMOTE, --remote REMOTE Look in the specified remote server

local HELP_OPTIONS=('(-)'{-h,--help}'[show help message and exit]')
local HELP_OPTIONS_PROFILE=('(-)'{-h,--help}'[show (profile) help message and exit]')

local INSTALL_OPTIONS=(
    {-if,--install-folder}'[Directory containing the conaninfo.txt and conanbuildinfo.txt files]:install dir:_files -/'
)

local JSON_OPTIONS=({-j,--json}'[Path to a json file where the information will be written]:file:_files *.json')
local RAW_OPTIONS=({-raw,--raw}'[Do not decorate the text]')

local UPDATE_OPTIONS=({-u,--update}'[Check updates exist from upstream remotes]')

local LOCKFILE_OPTIONS=({-l,--lockfile}"[Path to a lockfile or folder containing 'conan.lock']:lockfile:_files -g \*.lock")
#todo: "can be updated if packages change"

# todo: cache it!
typeset -agU conan_profiles
# array, global, unique (no duplicates),
typeset -agU conan_profile_items
local INSTALL_FOLDER_OPTIONS=({-if,--install-folder}'[Directory containing the conaninfo.txt and conanbuildinfo.txt files]:install dir:_files -/')

local BUILD_FOLDER_OPTIONS=({-bf,--build-folder}'[Build folder]:build directories:_files -/')


local PACKAGE_FOLDER_OPTIONS=({-pf,--package-folder}'[Directory to install the package]:package folder:_files -/' )

local SOURCE_FOLDER_OPTIONS=(
    {-sf,--source-folder}"[Directory containing the sources. Defaulted to the conanfile's directory]:source:_files -/"
    # {-sf,--source-folder}"[Directory containing the sources. (default conanfile\'s directory)]:source dir:_files -/"
    )
local QUERY_OPTIONS=(
    {-q,--query}"[Packages query: 'os=Windows AND (arch=x86 OR compiler=gcc)'. The 'pattern_or_reference' parameter]"
    )

local MANIFEST_OPTIONS=(
    # fixme: avoid the same one!
    '(-mi --manifests-interactive -m --manifests)'{-m,--manifests}'[Install dependencies manifests in folder for later]::manifests:_files'
    '(-mi --manifests-interactive -m --manifests)'{-mi,--manifests-interactive}'[Install dependencies manifests in folder for later..]::manifests:_files'
)

local VERIFY_OPTIONS=(
    {-v,--verify}'[Verify dependencies manifests against stored ones]'
)

## value completions:
local conan_build_policies=(never missing cascade outdated) # all
local BUILD_WHAT_OPTIONS=(
    {-b,--build}"+[What to re-build: all, --build=never Never build, --build=missing,--build=cascade  --build=outdated, --build=pattern]:: :($conan_build_policies)"
  # {-b,--build}'[Given a build policy, return an ordered list of packages that would be built from sources during the install command]: :($conan_build_policies)'
)

_conan_path_or_reference() {
    echo "choose " >> $LOG_FILE
    _conan_local_packages && return 0
    _conan_path
    # _files -/
}

# conan get ref file
_conan_path_inside()
{
}

# complete filename of the recipe.
_conan_path() {
    # path                  Path to a folder containing a conanfile.py or to a
    local state_description="$1"

    local expl
    # _description [ -x ] [ -12VJ ] TAG NAME DESCR [ SPEC ... ]
    #             TAG  NAME  DESCR         SPEC?
    echo "For $state_description $1"  >>$LOG_FILE

    _description -x conan_recipes expl "$state_description"
    echo "pass to compadd: $expl[@]"  >>$LOG_FILE
    # -J conan_recipes -X Completing %F{red}-J%f
    _path_files -g 'conanfile.p?' "$expl[*]"
    # -W '.'
    # -F '*~'
    #'conanfile.\{txt,py\}'
    # _path_files -/ .
}

_conan_generator()
{
    local generators=(cmake b2 makefile)
    compadd -- $generators
}

# todo: caching policy
_conan_profile_name(){
    # integer ret=1
    local cache_identifier="conan-profiles"

    # if ( [[ ${(P)+perl_modules} -eq 0 ]] || _cache_invalid ${perl_modules#_} ) &&

    if [[ "$#conan_profiles" = 0 ]] ||
           ! _retrieve_cache $cache_identifier
    then
        # see _perl_modules in
        # _cache_invalid
        echo "reading in the profiles" >>$LOG_FILE
        conan_profiles=( $(eval $CONAN_CMD[@] profile list 2>>$LOG_FILE) )
        _store_cache $cache_identifier conan_profiles
    fi

    foreach line ($conan_profiles) {
        # compadd -a AVAILABLE
        # echo "adding $line" >> $LOG_FILE
        compadd -- $line && ret=0
    }
#     return $ret
}

_conan_remote() {
    local conan_remotes=( $(eval $CONAN_CMD[@] remote list --raw|cut -f 1 -d ' ') )

    foreach line ($conan_remotes) {
        # compadd -a AVAILABLE
        # echo "adding $line" >> $LOG_FILE
        compadd -- $line && ret=0
    }
}

_conan_options()
{
    local recipe="."
    typeset -a package_options

    package_options=( $(eval $CONAN_CMD inspect -a options $recipe 2>/dev/null |
                            sed -n  's/^ \+\([^ :].*\):.*$/\1/;T;p') )

    foreach line ($package_options[2,-1]) {
        # compadd -a AVAILABLE
        # echo "adding $line" >> $LOG_FILE
        compadd -- "$line" && ret=0
    }
}




# xxx=y<TAB>
_conan_options_value()
{
    local recipe="."

    typeset -a package_options
    package_options=( $(eval $CONAN_CMD inspect -a options $recipe 2>/dev/null |
                            sed -n  's/^ \+\([^ :].*\):.*$/\1/;T;p') )

    foreach line ($package_options[2,-1]) {
        # compadd -a AVAILABLE
        # echo "adding $line" >> $LOG_FILE
        compadd -- "$line" && ret=0
    }
}

_conan_setting()
{
    local description=(
        "compiler:compiler"
        "compiler.libcxx:libc++"
        "compiler.version:"
        "build_type"
    )

    _describe "override setting" description

    # _values -s = 'description' \
    #         '*foo[bar]' \
    #         '(two)*one[number]:first count:' \
    #         'two[another number]::second count:(1 2 3)'

}

### Separate `Commands'

_conan-alias() {
    _arguments -C \
    $HELP_OPTIONS \
    ':reference' \
    ':target'
}

_conan-build () {
    _arguments -C \
               $HELP_OPTIONS \
               {-c,--configure}'[Execute the configuration step]' \
               {-i,--install}'[Execute the install step ...]' \
               {-t,--test}'[Execute the install step ...]' \
               $BUILD_FOLDER_OPTIONS \
               $INSTALL_OPTIONS \
               $PACKAGE_FOLDER_OPTIONS \
               $SOURCE_FOLDER_OPTIONS \
               '::_files'

    # source -> contains .git?

    # -i should_install=True). When specified, configure/build/test won't run
    # unless --configure/--build/--test specified


    # -c: (variable should_configure=True). When specified, build/install/test
    # won't run unless --build/--install/--test specified
}

# prepend each with log.
local log_items=(run_to_output run_to_file level print_run_commands)
local general_items=(default_profile
                     compression_level
                     sysrequires_sudo
                     request_timeout
                     revisions_enabled
                     default_package_id_mode
                    )

local storage_items=(path)
_conan_config_item() {
    local all=( ${log_items/#/log.}
                ${general_items/#/general.}
                ${storage_items/#/storage.}
                proxies
                hooks
              )

    _multi_parts '.' all
}

_conan-config-set() {
    _arguments -C \
               $HELP_OPTIONS \
               ":config item:_conan_config_item"
    # todo: =value
}

_conan-config-get() {
    _arguments -C \
               $HELP_OPTIONS \
               ":config item:_conan_config_item"
}

_conan-config() {
    local context state state_descr line
    typeset -A opt_args

    _arguments -C \
               $HELP_OPTIONS \
               ':subcommand:(get home install rm set)' \
               '*:: :->subcommand' && return 0

    # todo: how to continue?
    # mmc: message must be present. Otherwise?

    case $state in
        (subcommand)
            # Modify the curcontext parameter for an action of the form
            local cmd=$words[1]
            local curcontext=${curcontext%:*:*}:conan-config-$cmd:
            if ! _call_function ret _conan-config-$cmd; then
                _message "unknown sub-command: $cmd"
            else
                return $ret
            fi
            ;;
    esac
}

_conan-copy()
{
    _conan_local_packages
}

_conan-create()
{
    local arguments=(
        $HELP_OPTIONS
               $JSON_OPTIONS
               $KEEP_SOURCE_OPTIONS
               {-kb,--keep-build}'[Do not remove the build folder in local cache.]'
               # implies -ks
               {-ne,--not-export}'[Do not export the conanfile.py (use older?)]'
               {-tbf,--test-build-folder}'[TEST_BUILD_FOLDER]:test folder:_files -/'
               $MANIFEST_OPTIONS
               $VERIFY_OPTIONS
               $BUILD_WHAT_OPTIONS
               $ENV_OPTIONS
               $PROFILE_OPTIONS
               $OPTIONS_OPTIONS
               $SETTING_OPTIONS
               $REMOTE_OPTIONS
               $UPDATE_OPTIONS
               $LOCKFILE_OPTIONS
    )
  #                   path [reference]
    _arguments -C $arguments
}

_conan-download() {
    _arguments -C \
               $HELP_OPTIONS \
               {-p,--package}'[Force install specified package ID]' \
               $REMOTE_OPTIONS \
               {-re,--recipe}'[Downloads only the recipe]'
    #todo: '::_conan_reference
}

_conan-editable-add()
{
    echo "_conan-editable-add" >> $LOG_FILE
    _arguments -C \
               ": : _conan_path" \
               ": :_conan_reference" && return 0
}

_conan-editable-remove()
{
    _arguments -C \
               ": :_conan_reference" && return 0
}

_conan-editable()
{
    _arguments -C \
               $HELP_OPTIONS \
               ': :(add remove list)' \
               '*:: :->subcommand' && return 0

    case $state in
        (subcommand)
            # Modify the curcontext parameter for an action of the form
            local cmd=$words[1]
            local curcontext=${curcontext%:*:*}:conan-editable-$cmd:
            if ! _call_function ret _conan-editable-$cmd; then
                _message "unknown sub-command: $cmd"
            else
                return $ret
            fi
            ;;
    esac

}

_conan-export() {
    _arguments -C \
        $HELP_OPTIONS \
        $KEEP_SOURCE_OPTIONS \
        $LOCKFILE_OPTIONS \
        ':conan recipe: _conan_path' \
        ': :_conan_reference'
    # 1.20+
    #  '--ignore-dirty:'
}

_conan-export-pkg() {
    local all_args=(
        $HELP_OPTIONS
        $BUILD_FOLDER_OPTIONS
        $ENV_OPTIONS
        {-f,--force}'[Overwrite existing package if existing]'
        $INSTALL_FOLDER_OPTIONS
        $OPTIONS_OPTIONS
        $SETTING_OPTIONS
        $SOURCE_FOLDER_OPTIONS
        $JSON_OPTIONS
        # Path to a json file where the install information will be written
        $LOCKFILE_OPTIONS
    )
    _arguments -C $all_args
}

_conan-get()
{
    _arguments -C \
               $HELP_OPTIONS \
               $REMOTE_OPTIONS \
               $RAW_OPTIONS \
               ': :_conan_reference' \
               ': : _conan_path_inside'  && return 0
}


_conan-graph()
{
    local curcontext=$curcontext \
          state line ret=1
    declare -A opt_args

    _arguments -C \
               $HELP_OPTIONS \
               ': :->command' \
               '*:: :->option-or-argument' && ret=0

    case $state in
        (command)
            declare -a commands
            commands=(
                # these  have a subcommand help!
                "update-lock:merge two lockfiles"
                "build-order:Returns build-order"
                "lock:create a lockfile"
            )

            _describe \
                "Generates and manipulates lock files." commands && ret=0
            ;;
        (option-or-argument)
            curcontext=${curcontext%:*}-$line[1]:

            case $line[1] in
                (lock)
                    _arguments -C \
                               $HELP_OPTIONS \
                               $BUILD_WHAT_OPTIONS \
                               $ENV_OPTIONS \
                               $OPTIONS_OPTIONS \
                               $PROFILE_OPTIONS \
                               $REMOTE_OPTIONS \
                               $SETTING_OPTIONS \
                               $UPDATE_OPTIONS \
                               $LOCKFILE_OPTIONS \
                               ': :_conan_path_or_reference' \
                        && ret=0
                    ;;
                (update-lock)
                    _arguments -C \
                               ': :_files -g \*.lock' \
                               ': :_files -g \*.lock' && ret=0
                    ;;
                (build-order)
                    _arguments -C \
                               $HELP_OPTIONS \
                               $BUILD_WHAT_OPTIONS \
                               $JSON_OPTIONS \
                               ': :_files -g \*.lock' && ret=0
                    ;;
            esac
            ;;
    esac

    return ret
}

_conan-help()
{
    local cmdtype
    local -a allcmds

    for cmdtype in $cmdtypes; do
        set -A ${cmdtype}_m ${(P)cmdtype%%:*}
        local allcmds+=( ${(P)${:-${cmdtype}_m}} )
        #set -A ${cmdtype}_m ${(P)cmdtype%%:*}
        compadd $allcmds
        # "$expl[@]" -a misc_cmds
    done
}

_conan-info()
{
    local arguments=(
        $HELP_OPTIONS
        '--paths[Show package paths in local cache]'
        {-bo,--build-order}'[given a modified reference, return an ordered list to build (CI)]'
        {-g,--graph}'[Creates file with project dependencies graph.DOT/HTML depending on the filename]: :_files -g \*.lock'
        $INSTALL_OPTIONS
        $JSON_OPTIONS
        {-n,--only}'[Show only the specified fields]: : (id build_id remote url license requires update required date author)'
        # todo: '--paths' information can also be filtered with options
                        # (export_folder build_folder package_folder source_folder)
                        #Use '--only None' to show only references.
        '--package-filter[Print information only for packages that match the MyPackage*]'
        {-db,--dry-build}'[Apply the --build argument to output the information, as it would be done by the install command]'
        $BUILD_WHAT_OPTIONS
        $ENV_OPTIONS
        $PROFILE_OPTIONS
        $OPTIONS_OPTIONS
        $SETTING_OPTIONS
        $REMOTE_OPTIONS
        $UPDATE_OPTIONS
        $LOCKFILE_OPTIONS # fixme: Lockfile can be updated if packages change
        ': :_conan_path_or_reference'
    )
    _arguments -C $arguments
}


local conanfile_attributes=(options default_options name version)

_conan-inspect() {
    # "Displays conanfile attributes, like name, version and options. Works locally, in local cache and remote."
    _arguments -C $HELP_OPTIONS \
               {-a,--attribute}"[The attribute to be displayed, e.g 'name']: :($conanfile_attributes)" \
               $REMOTE_OPTIONS \
               $JSON_OPTIONS \
               '--raw[Print just the value of the requested attribute]'
}

# todo: I want to complete the options immediately, not after double TAB, still hoping for a filename?
_conan-install(){
    local context state state_descr line
    typeset -A opt_args

    _arguments -C \
        $HELP_OPTIONS \
        {-g,--generator}'[Generator]:generator:_conan_generator' \
        \
        $INSTALL_FOLDER_OPTIONS \
        $MANIFEST_OPTIONS \
        $VERIFY_OPTIONS \
        '--no-imports[avoid running imports]'  \
        $JSON_OPTIONS \
        $BUILD_WHAT_OPTIONS \
        $ENV_OPTIONS \
        $OPTIONS_OPTIONS \
        $PROFILE_OPTIONS \
        $REMOTE_OPTIONS \
        $SETTING_OPTIONS \
        $UPDATE_OPTIONS \
        $LOCKFILE_OPTIONS \
        ': :_conan_path_or_reference'
}

_conan-package()
{
    local arguments=(
        $HELP_OPTIONS
        $INSTALL_FOLDER_OPTIONS
        # Defaulted to --build-folder
        $BUILD_FOLDER_OPTIONS
        # Defaulted to the '{build_folder}/package' folder. A relative path can
        $PACKAGE_FOLDER_OPTIONS
        $SOURCE_FOLDER_OPTIONS
    )

    _arguments -C $arguments ': :_conan_path'
    #  path Path to a folder containing a conanfile.py or to a recipe file e.g., my_folder/conanfile.py
}

# I need
_conan_profile_item()
{
    # choose one:
    local cache_identifier="conan-profile-items"

    if ! _retrieve_cache $cache_identifier;
    then
        conan_profile_items=(
            $( ~/tests/yaml/token ~/conan-repo/.conan/settings.yml |
                   sed -n -e 's/saving key /settings./p' ) )

        _store_cache $cache_identifier conan_profile_items
    fi

    _multi_parts '.' conan_profile_items
}

_conan-profile-remove()
{
    local context state state_descr line
    typeset -A opt_args

    _arguments -C ':profile-item:_conan_profile_item' \
               ': : _conan_profile_name' && ret=0
}

_conan-profile-update()
{
    local context state state_descr line
    typeset -A opt_args

    # todo  =value
    _arguments -C ':profile-item:_conan_profile_item' \
               ': : _conan_profile_name' && ret=0
}

_conan-profile-list()
{
    local context state state_descr line
    typeset -A opt_args

    _arguments -C $HELP_OPTIONS
}

# MATCHSPEC       r:|[_-]=* r:|=*
_conan-profile-get()
{
    local context state state_descr line
    typeset -A opt_args

    # -O NAME .. Pass the elements of the array NAME as arguments to functions
    # called to execute ACTIONs.  This is discussed in detail below.
    _arguments -C : \
               ':profile-item:_conan_profile_item' \
               ': : _conan_profile_name'
}

_conan-profile-show()
{
    local context state state_descr line
    typeset -A opt_args

    echo "_conan-profile-show: $curcontext $CURRENT" >> $LOG_FILE
    _arguments -C $HELP_OPTIONS \
               ':profile-name: _conan_profile_name'
}

_conan-profile()
{
    local context state state_descr line
    typeset -A opt_args
    # integer ret=1
    _arguments -C \
               $HELP_OPTIONS_PROFILE \
               ': :(list show new update get remove)' \
               '*:: :->argument' && return 0

    # dispatch to sub-handler!
    case $state in
        (argument)
            # context -> last word?
            # :complete:conan-profile:argument-2
            # so rewrite it to:
            # :complete:conan-profile-show:
            # assert $words[1] = profile
            #
            local curcontext=${curcontext%:*:*}:conan-profile-$words[1]:

            echo "modified to $curcontext" >> $LOG_FILE

            # _tags:comptags:36: nesting level too deep
            if ! _call_function ret _conan-profile-$words[1]; then
                _message "unknown sub-command: $words[1]"
            fi
            ;;
        *)
            _message "unknown sub-command: $words[1]"
            ;;
    esac
    return $ret
}

_conan-remove()
{
    local arguments=(
               $HELP_OPTIONS
               {-b,--builds}''
               {-f,--force}'[Remove without requesting a confirmation]'
               {-l,--locks}'[Remove locks]'
               {-o,--outdated}'[Remove only outdated from recipe packages. only used with a reference!]'
               {-p,--packages}'[Select package to remove specifying the package ID]'
               $QUERY_OPTIONS
               $REMOTE_OPTIONS
               {-s,--src}'[Remove source folders]'
               {-t,--system-reqs}'[Remove system_reqs folders]'
               ': : _conan_pattern_or_reference'
    )
    _arguments -C $arguments
}

# todo: connect it to conan-remote:
_conan-remote-list()
{
    : \
    $HELP_OPTIONS \
    $RAW_OPTIONS
}

_conan-remote()
{
    # how to add explanation?
    typeset -a descriptions
    descriptions=(
        "list:List current remotes"
        "add:Add a remote"
        "remove:Remove a remote"
        "update:Update the remote url"
        "rename:Update the remote name"
        "list_ref:List the package recipes and its associated remotes"
        "add_ref:Associate a recipe's reference to a remote"
        "remove_ref:Dissociate a recipe's reference and its remote"
        "update_ref:Update the remote associated with a package recipe"
        "list_pref:List the package binaries and its associated remotes"
        "add_pref:Associate a package reference to a remote"
        "remove_pref:Dissociate a package's reference and its remote"
        "update_pref:Update the remote associated with a binary package"
        "clean:Clean the list of remotes and all recipe-remoteassociations"
        "enable:Enable a remote"
        "disable:Disable a remote"
    )
    _describe "Manages the remote list and the package recipes associated to a remote." descriptions

    #    _arguments -C ': :(list add remove update rename list_ref add_ref list_pref add_pref remove_pref update_pref clean enable disable)'
    # List the package recipes and its associated remotes
}

_conan-search()
{
    local context state state_descr line
    # typeset -A opt_args

    local arguments=(
        $HELP_OPTIONS
        {-o,--outdated}'[Show only outdated from recipe packages.]'
        $QUERY_OPTIONS
        $REMOTE_OPTIONS
        '--case-sensitive[Make a case-sensitive search.]'
        '--raw[Print just the list of recipes]'
        '--table[Outputs html file with a table of binaries. Only valid for a reference search]'
        #TABLE
        $JSON_OPTIONS
        {-rev,--revisions}'[Get a list of revisions for a reference or a package]'

        ': : _conan_pattern_or_reference'
    )

    _arguments -C $arguments
}

_conan-source()
{
    local context state state_descr line
    # The elements of the array state_descr are assigned the corresponding MESSAGE
    # field from each OPTARG containing such an ACTION.

    local source_options=(
        $HELP_OPTIONS
        $SOURCE_FOLDER_OPTIONS
        # [Destination directory. Defaulted to current directory]
        $INSTALL_FOLDER_OPTIONS
        )
    _arguments -C $source_options \
               ":conan path: _conan_path 'conan path'"
}

_conan-upload()
{
    local context state state_descr line

    local upload_options=(
        $HELP_OPTIONS
        # -p  obsolete!
        $QUERY_OPTIONS
        $REMOTE_OPTIONS
        '--all[Upload both package recipe and packages]'
        # exclusive:
        '(--force)--skip-upload[Do not upload anything, just run the checks and the compression]'
        '(--skip-upload)--force[Do not check conan recipe date, override remote with local]'
        #
        '--check[Perform an integrity check, using the manifests, before upload]'
        {-c,--confirm}'[Upload all matching recipes without confirmation]'
        '--retry[In case of fail retries to upload again the specified times]: RETRY'

        '--retry-wait[Wait specified seconds before retry again]:seconds:'

        '-no[Uploads package only if recipe is the same as the remote one]: :(all recipe)'
        '--no-overwrite: :(all recipe)'
        $JSON_OPTIONS
        '--parallel[Upload files in parallel using multiple threads(default 8)]'
    )
    _arguments -C $upload_options \
               ":conan path: _conan_pattern_or_reference"
}


local context state state_descr line
typeset -A opt_args

integer ret=1
# Here we treat only the first work. The rest is delegated to subcommand handler.
_arguments -C \
           {-v,--version}'[get the Conan version]:' \
           $HELP_OPTIONS \
           '(-): :->command' \
           '(-)*:: :->option-or-argument' && return 0
#                ^?? optional. message must be non-empty (*:::)
# (-) no option afterwards!
case $state in
    (command)
        _conan_commands1 && ret=0
        ;;
    (option-or-argument)
        # In addition to $state and $state_descr, _arguments also sets
        #  the global parameters 'context', 'line' and 'opt_args' as

     # Furthermore, during the evaluation of the ACTION the context name
     # in the curcontext parameter is altered to append the same string
     # that is stored in the context parameter.

     # The option -C tells _arguments to modify the curcontext parameter
     # for an action of the form '->STATE'.  This is the standard

        # Modify the curcontext parameter for an action of the form
        #  '->STATE'.  This is discussed in detail below.
        curcontext=${curcontext%:*:*}:conan-$words[1]:

        if ! _call_function ret _conan-$words[1]; then
            _message "unknown sub-command: $words[1]"
        else
            ret=0
        fi
esac

# compadd "$expl[@]" -O allmatching -a allcmds
# _alternative $alts
# alts+=( "${cmdtype//_/-}:${${cmdtype//_/ }%%(e|)s}:compadd ${(e)disp} -a ${cmdtype}_m" )

# compadd ${(e)disp} -a ${cmdtype}_m
return ret
