#compdef mvn mvnDebug
# ------------------------------------------------------------------------------
# Copyright (c) 2010-2011 Julien Nicoulaud <julien.nicoulaud@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# ------------------------------------------------------------------------------
# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
# vim: ft=zsh sw=2 ts=2 et
# ------------------------------------------------------------------------------


_mvn() {

  local curcontext="$curcontext" state line cmds ret=1 mvn_version

  if _pick_variant maven3=Maven\ 3 maven2 --version; then
    version_specific_options=(
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -T --threads)'{-T+,--threads=}'[Thread count, for instance 2.0C where C is core multiplied]:thread-count:->thread-count'
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -t --toolchains)'{-t,--toolchains=}'[Alternate path for the user toolchains file]:toolchains-file:->toolchains-file'
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -l --log-file)'{-l,--log-file=}'[Log file to where all build output will go.]:log-file:->log-file'
    )
  else
    version_specific_options=(
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -cpu --check-plugin-updates -up --update-plugins -npu --no-plugin-updates -o --offline)'{-cpu,--check-plugin-updates,-up,--update-plugins}'[Force upToDate check for any relevant registered plugins.]'
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -npr --no-plugin-registry)'{-npr,--no-plugin-registry}'[Don'\''t use ~/.m2/plugin-registry.xml for plugin versions.]'
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -npu --no-plugin-updates -cpu --check-plugin-updates -up --update-plugins)'{-npu,--no-plugin-updates}'[Suppress upToDate check for any relevant registered plugins.]'
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -r --reactor)'{-r,--reactor}'[Dynamically build reactor from subdirectories.]:reactor:->reactor'
    )
  fi

  # FIXME -am and -amd should only be added if -pl is present
  _arguments -C \
    '(- 1 *)'{-h,--help}'[Display help information]' \
    '(- 1 *)'{-v,--version}'[Display version information]' \
    '(- 1 *)'{-emp,--encrypt-master-password}'[Encrypt master security password]' \
    '(- 1 *)'{-ep,--encrypt-password}'[Encrypt server password]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -B --batch-mode)'{-B,--batch-mode}'[Run in non-interactive (batch) mode]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -V --show-version)'{-V,--show-version}'[Display version information WITHOUT stopping build]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -q --quiet -X --debug)'{-q,--quiet}'[Quiet output - only show errors]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -X --debug -q --quiet)'{-X,--debug}'[Produce execution debug output]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -N --non-recursive)'{-N,--non-recursive}'[Do not recurse into sub-projects]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -C --strict-checksums -c --lax-checksums)'{-C,--strict-checksums}'[Fail the build if checksums don'\''t match]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -c --lax-checksums -C --strict-checksums)'{-c,--lax-checksums}'[Warn if checksums don'\''t match]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -e --errors)'{-e,--errors}'[Produce execution error messages]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -f --file)'{-f,--file=}'[Force the use of an alternate POM file.]:pom-file:->pom-file' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -s --settings)'{-s,--settings=}'[Alternate path for the user settings file]:settings-file:->settings-file' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -gs --global-settings)'{-gs,--global-settings=}'[Alternate path for the global settings file]:settings-file:->settings-file' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -fae --fail-at-end -ff --fail-fast -fn --fail-never)'{-fae,--fail-at-end}'[Only fail the build afterwards; allow all non-impacted builds to continue]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -ff --fail-fast -fae --fail-at-end -fn --fail-never)'{-ff,--fail-fast}'[Stop at first failure in reactorized builds]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -fn --fail-never -fae --fail-at-end -ff --fail-fast)'{-fn,--fail-never}'[NEVER fail the build, regardless of project result]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -P --activate-profiles)'{-P,--activate-profiles=}'[Comma-delimited list of profiles to activate]:profile:->profile' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -pl --projects)'{-pl,--projects=}'[Build specified reactor projects instead of all projects. A project can be specified by groupId:artifactId or by its relative path.]:project-list:->project-list' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -am --also-make)'{-am,--also-make}'[If project list is specified, also build projects required by the list]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -amd --also-make-dependents)'{-amd,--also-make-dependents}'[If project list is specified, also build projects that depend on projects on the list]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -rf --resume-from)'{-rf,--resume-from=}'[Resume reactor from specified project]:project:->project' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -o --offline -U --update-snapshots -cpu --check-plugin-updates -up --update-plugins)'{-o,--offline}'[Work offline]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -U --update-snapshots -nsu --no-snapshot-updates -o --offline)'{-U,--update-snapshots}'[Forces a check for updated releases and snapshots on remote repositories]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password -nsu --no-snapshot-updates -U --update-snapshots -o --offline)'{-nsu,--no-snapshot-updates}'[Supress SNAPSHOT updates]' \
    '(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password)'{-D-,--define}'[Define a system property]:property:->property' \
    "$version_specific_options[@]" \
    '*: :->args' \
    && ret=0

  if [[ -n "$state" ]]; then
    case "$state" in
      pom-file)
        _wanted pom-files expl pom-file _mvn_pom_files && ret=0
        ;;
      settings-file)
        _wanted settings-files expl settings-file _mvn_settings_files && ret=0
        ;;
      profile)
        _wanted profiles expl profile _mvn_profiles && ret=0
        ;;
      project)
        _wanted projects expl project _mvn_projects && ret=0
        ;;
      project-list)
        _wanted project-lists expl project-list _mvn_project_lists && ret=0
        ;;
      log-file)
        _wanted log-files expl log-file _mvn_log_files && ret=0
        ;;
      toolchains-file)
        _wanted toolchains-files expl toolchains-file _mvn_toolchains_files && ret=0
        ;;
      property)
        _wanted properties expl property _mvn_properties && ret=0
        ;;
      reactor)
        _wanted reactors expl reactor _mvn_reactors && ret=0
        ;;
      thread-count)
        _wanted thread-counts expl thread-count _mvn_thread_counts && ret=0
        ;;
      args)
        _alternative \
          'phases:phase:_mvn_phases' \
          'goals:goal:_mvn_goals' \
          && ret=0
        ;;
    esac
  fi
}

_mvn_phases() {
  # FIXME Make it complete advanced phases only if the user started typing it. 
  local ret=1
  _values 'phase' \
    'pre-clean[executes\ processes\ needed\ prior\ to\ the\ actual\ project\ cleaning.]' \
    'clean[remove\ all\ files\ generated\ by\ the\ previous\ build.]' \
    'post-clean[executes\ processes\ needed\ to\ finalize\ the\ project\ cleaning.]' \
    'validate[validate\ the\ project\ is\ correct\ and\ all\ necessary\ information\ is\ available.]' \
    'initialize[initialize\ build\ state,\ e.g.\ set\ properties\ or\ create\ directories.]' \
    'generate-sources[generate\ any\ source\ code\ for\ inclusion\ in\ compilation.]' \
    'process-sources[process\ the\ source\ code,\ for\ example\ to\ filter\ any\ values.]' \
    'generate-resources[generate\ resources\ for\ inclusion\ in\ the\ package.]' \
    'process-resources[copy\ and\ process\ the\ resources\ into\ the\ destination\ directory,\ ready\ for\ packaging.]' \
    'compile[compile\ the\ source\ code\ of\ the\ project.]' \
    'process-classes[post-process\ the\ generated\ files\ from\ compilation,\ for\ example\ to\ do\ bytecode\ enhancement\ on\ Java\ classes.]' \
    'generate-test-sources[generate\ any\ test\ source\ code\ for\ inclusion\ in\ compilation.]' \
    'process-test-sources[process\ the\ test\ source\ code,\ for\ example\ to\ filter\ any\ values.]' \
    'generate-test-resources[create\ resources\ for\ testing.]' \
    'process-test-resources[copy\ and\ process\ the\ resources\ into\ the\ test\ destination\ directory.]' \
    'test-compile[compile\ the\ test\ source\ code\ into\ the\ test\ destination\ directory.]' \
    'process-test-classes[post-process\ the\ generated\ files\ from\ test\ compilation,\ for\ example\ to\ do\ bytecode\ enhancement\ on\ Java\ classes.\ For\ Maven\ 2.0.5\ and\ above.]' \
    'test[run\ tests\ using\ a\ suitable\ unit\ testing\ framework.\ These\ tests\ should\ not\ require\ the\ code\ be\ packaged\ or\ deployed.]' \
    'prepare-package[perform\ any\ operations\ necessary\ to\ prepare\ a\ package\ before\ the\ actual\ packaging.\ This\ often\ results\ in\ an\ unpacked,\ processed\ version\ of\ the\ package.]' \
    'package[take\ the\ compiled\ code\ and\ package\ it\ in\ its\ distributable\ format,\ such\ as\ a\ JAR.]' \
    'pre-integration-test[perform\ actions\ required\ before\ integration\ tests\ are\ executed.\ This\ may\ involve\ things\ such\ as\ setting\ up\ the\ required\ environment.]' \
    'integration-test[process\ and\ deploy\ the\ package\ if\ necessary\ into\ an\ environment\ where\ integration\ tests\ can\ be\ run.]' \
    'post-integration-test[perform\ actions\ required\ after\ integration\ tests\ have\ been\ executed.\ This\ may\ including\ cleaning\ up\ the\ environment.]' \
    'verify[run\ any\ checks\ to\ verify\ the\ package\ is\ valid\ and\ meets\ quality\ criteria.]' \
    'install[install\ the\ package\ into\ the\ local\ repository,\ for\ use\ as\ a\ dependency\ in\ other\ projects\ locally.]' \
    'deploy[done\ in\ an\ integration\ or\ release\ environment,\ copies\ the\ final\ package\ to\ the\ remote\ repository\ for\ sharing\ with\ other\ developers\ and\ projects.]' \
    'pre-site[executes\ processes\ needed\ prior\ to\ the\ actual\ project\ site\ generation.]' \
    'site[generates\ the\ projects\ site\ documentation.]' \
    'post-site[executes\ processes\ needed\ to\ finalize\ the\ site\ generation,\ and\ to\ prepare\ for\ site\ deployment.]' \
    'site-deploy[deploys\ the\ generated\ site\ documentation\ to\ the\ specified\ web\ server.]' \
    && ret=0
}

_mvn_goals(){
  # TODO to be implemented
  local ret=1
  #goals=(
  #  'release:clean'
  #  'release:prepare'
  #  'release:prepare-with-pom'
  #  'release:rollback'
  #  'release:perform'
  #  'release:stage'
  #  'release:branch'
  #  'release:update-versions'
  #)
  #_multi_parts ':' goals
  #_sep_parts
}

_mvn_reactors(){
  # FIXME No idea what kind of value the "--reactor" is supposed to take
  local ret=1
  _message -e reactors 'reactor' && ret=0
}

_mvn_thread_counts(){
  _message -e thread-counts 'thread count' && ret=0
}

_mvn_pom_files(){
  local ret=1
  _files -g '*pom*\.xml*' && ret=0
}

_mvn_toolchains_files(){
  local ret=1
  _files && ret=0
}

_mvn_settings_files(){
  local ret=1
  _files -g '*settings*\.xml*' && ret=0
}

_mvn_log_files(){
  local ret=1
  _files && ret=0
}

_mvn_profiles() {
  # FIXME This could be done more reliabily using "mvn -f pom-file -N help:all-profiles" + caching.
  local ret=1 in_profile=false profiles
  for file in pom.xml ~/.m2/settings.xml; do
    for line in $(command cat $file 2>/dev/null); do
      if $in_profile && [[ $line == *'<id>'* ]]; then
        profiles+=("${${line##*<id>}%%<\/id>*}[in $file]")
        in_profile=false
      elif [[ $line == *'<profile>'* ]]; then
        in_profile=true
      fi
    done
  done
  _values -s , 'profile' "$profiles[@]" && ret=0
}

_mvn_projects() {
  # TODO projects can also be given in the form [groupId:]artifactId.
  local ret=1 projects
  for project in $(command ls */(**~target)/pom.xml 2>/dev/null); do # FIXME Only recurse when needed ?
    projects+=("$(dirname $project)[]") # TODO: Add project name as description
  done
  _values 'project list' "$projects[@]" && ret=0
}

_mvn_project_lists() {
  # TODO projects can also be given in the form [groupId:]artifactId.
  local ret=1 projects
  for project in $(command ls */(**~target)/pom.xml 2>/dev/null); do # FIXME Only recurse when needed ?
    projects+=("$(dirname $project)[]") # TODO: Add project name as description
  done
  _values -s , 'project' "$projects[@]" && ret=0
}

_mvn_properties() {
  # TODO Complete some very common props like -DskipTests, etc.
  local ret=1
  _message -e property-names 'property name' && ret=0
}

_mvn "$@"