#compdef crystal

_crystal() {

_crystal_commands() {
  local -a commands
  commands=(
    "init:generate new crystal project"
    "build:build an executable"
    "clear_cache:clear the compiler cache"
    "docs:generate documentation"
    "env:print Crystal environment information"
    "eval:eval code from args or standard input"
    "play:starts playground server"
    "run:build and run program"
    "spec:build and run specs (in spec directory)"
    "tool:run a tool"
    "help:show help"
    "version:show version"
  )
  _describe -t commands 'Crystal command' commands
}

local -a help_args; help_args=(
  '(-h --help)'{-h,--help}'[show help]'
)

local -a version_args; version_args=(
  '(-v --version)'{-v,--version}'[show version]' \
)

local -a no_color_args; no_color_args=(
  '(--no-color)--no-color[disable colored output]'
)

local -a prelude_args; prelude_args=(
  '(--prelude)--prelude[use given file as prelude]'
)

local -a exec_args; exec_args=(
  '(\*)'{-D+,--define=}'[define a compile-time flag]:' \
  '(--error-trace)--error-trace[show full error trace]' \
  '(-s --stats)'{-s,--stats}'[enable statistics output]' \
  '(-t --time)'{-t,--time}'[enable execution time output]' \
  '(-p --progress)'{-p,--progress}'[enable progress output]'
)

local -a format_args; format_args=(
  '(-f --format)'{-f,--format}'[output format text (default) or json]:'
)

local -a debug_args; debug_args=(
  '(-d --debug)'{-d,--debug}'[add full symbolic debug info]' \
  '(--no-debug)--no-debug[skip any symbolic debug info]'
)

local -a release_args; release_args=(
  '(--release)--release[compile in release mode]'
)

local -a cursor_args; cursor_args=(
  '(-c --cursor)'{-c,--cursor}'[cursor location with LOC as path/to/file.cr:line:column]:LOC'
)

local -a include_exclude_args; include_exclude_args=(
  '(-i --include)'{-i,--include}'[Include path in output]' \
  '(-i --exclude)'{-i,--exclude}'[Exclude path in output]'
)

local -a stdin_filename_args; stdin_filename_args=(
  '(--stdin-filename)--stdin-filename[source file name to be read from STDIN]'
)

local -a programfile; programfile='*:Crystal File:_files -g "*.cr(.)"'

# TODO make 'emit' allow completion with more than one
local -a shared_run_build; shared_run_build=(
  $programfile \
  $help_args \
  $no_color_args \
  $prelude_args \
  $format_args \
  $exec_args \
  $debug_args \
  $release_args \
  '(--emit)--emit[comma separated list of types of output for the compiler to emit]:foo:(asm llvm-bc llvm-ir obj)' \
  '(--x86-asm-syntax)--x86-asm-syntax[X86 dialect for --emit-asm: att (default), intel]:' \
  "(--ll)--ll[dump ll to Crystal's cache directory ]" \
  '(--link-flags)--link-flags[additional flags to pass to the linker]:' \
  '(--mcpu)--mcpu[target specific cpu type]:' \
  '(--mattr)--mattr[target specific features]:' \
  "(--no-codegen)--no-codegen[don't do code generation]" \
  '(-o --output)'{-o,--output}'[output filename]:' \
  '(--single-module)--single-module[generate a single llvm module]' \
  '(--threads)--threads[maximum number of threads to use]' \
  '(--verbose)--verbose[display executed commands]' \
)

# TODO add help text for name and dir
_crystal-init() {
  _arguments \
    '1:type:(lib app)' \
    && ret=0
}

_crystal-build() {
  _arguments \
    $shared_run_build \
    '(--cross-compile)--cross-compile[cross-compile FLAGS]:' \
    '(--target)--target[target triple]:' \
    && ret=0
}

_crystal-env() {
  _arguments \
    '(--help)--help[prints help]' \
    '1:type:(CRYSTAL_CACHE_DIR CRYSTAL_PATH CRYSTAL_VERSION CRYSTAL_OPTS)' \
    && ret=0
}

_crystal-eval() {
  _arguments \
    $help_args \
    $no_color_args \
    $exec_args \
    $debug_args \
    $release_args \
    && ret=0
}

_crystal-play() {
  _arguments \
    $programfile \
    '(--port)--port[PORT]:' \
    '(--binding)--binding[HOST]:' \
    '(--verbose)--verbose[display detailed information of executed code]' \
    '(-h --help)'{-h,--help}'[show help]' \
    && ret=0
}

_crystal-run() {
  _arguments \
    $shared_run_build \
    && ret=0
}

_crystal-spec() {
  _arguments \
    $programfile \
    $help_args \
    $no_color_args \
    $exec_args \
    $debug_args \
    $release_args \
    && ret=0
}

_crystal-tool() {
  local curcontext="$curcontext" state line
  typeset -A opt_args

  _arguments -C \
    ':command:->command' \
    '*::options:->options'

  case $state in
    (command)
      local -a commands

      commands=(
        "context:show context for given location"
        "dependencies:show tree of required source files"
        "expand:show macro expansion for given location"
        "flags:print all macro 'flag?' values"
        "format:format project, directories and/or files"
        "hierarchy:show type hierarchy"
        "implementations:show implementations for given call in location"
        "macro_code_coverage:generate a macro code coverage report"
        "types:show type of main variables"
        "unreachable:show methods that are never called"
      )

      _describe -t commands 'Crystal tool command' commands

      _arguments $help_args
    ;;

    (options)
      case $line[1] in
        (context)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $format_args \
            $prelude_args \
            $stdin_filename_args \
            $cursor_args
        ;;

        (dependencies)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            '(-f --format)'{-f,--format}'[output format 'tree' (default), 'flat', 'dot', or 'mermaid']:' \
            $prelude_args \
            $stdin_filename_args \
            $include_exclude_args
        ;;

        (expand)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $format_args \
            $prelude_args \
            $stdin_filename_args \
            $cursor_args
        ;;

        (flags)
          _arguments \
            $programfile \
            $no_color_args \
            $help_args
        ;;

        (format)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $include_exclude_args \
            '(--check)--check[checks that formatting code produces no changes]' \
            '(--show-backtrace)--show-backtrace[show backtrace on a bug (used only for debugging)]'
        ;;

        (hierarchy)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $format_args \
            $prelude_args \
            $stdin_filename_args \
            '(-e)-e[filter types by NAME regex]:'
        ;;

        (implementations)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $format_args \
            $prelude_args \
            $cursor_args \
            $stdin_filename_args
        ;;

        (unreachable)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $include_exclude_args \
            '(-f --format)'{-f,--format}'[output format: text (default), json, csv, codecov]:' \
            $prelude_args \
            '(--check)--check[exits with error if there is any unreachable code]' \
            '(--tallies)--tallies[print reachable methods and their call counts as well]' \
            $stdin_filename_args
        ;;

        (macro_code_coverage)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $include_exclude_args \
            '(-f --format)'{-f,--format}'[output format: codecov (default)]:' \
            $prelude_args \
            $stdin_filename_args
        ;;

        (types)
          _arguments \
            $programfile \
            $help_args \
            $no_color_args \
            $exec_args \
            $format_args \
            $prelude_args \
            $stdin_filename_args
        ;;
      esac
    ;;
  esac
}

local curcontext=$curcontext ret=1
declare -A opt_args
_arguments -C \
  $help_args \
  $version_args \
  '1:sub-command: _alternative "subcommands:sub command:_crystal_commands" "files:file:_files -g \*.cr\(-.\)"' \
  '*::arg:->cmd' && ret=0
case $state in
  (cmd)
    if (( $CURRENT == 1 )); then
    else
      curcontext="${curcontext%:*:*}:crystal-$words[1]:"
      if ! _call_function ret _crystal-$words[1] ; then
          _default && ret=0
      fi
      return ret
    fi
    ;;
esac
}
_crystal
