#compdef todo

# {{{ sub commands common options variables
local common_options_help=(
	'(- :)--help[Show a help message and exit]'
)
local common_options_start=(
	{-s,--start=}'[When the task starts]:DATE:__todo_date'
)
local common_options_due=(
	{-d,--due=}'[When the task is due]:DATE:__todo_date'
)
local common_options_priority=(
	'--priority=[The priority for this todo]:PRIORITY:("low" "medium" "high")'
)
local common_options_interactive=(
	{-i,--interactive}'[Go into interactive mode before saving the task]'
)
local common_options_location=(
	'--location=[The location where this todo takes place]:LOCATION:'
)
# }}}
# {{{ option helper: color mode
__color_mode(){
	local modes=(
		"always:enable regardless of stdout"
		"auto:enable only when not on tty (default)"
		"never:disable colored output entirely"
	)
	_describe "mode" modes
}
# }}}
# {{{ general helper: set variable of path to configuration file
__todo_set_conf(){
	todoman_configuration_file=${XDG_CONFIG_DIR:-${HOME}/.config}/todoman/todoman.conf
	if [[ -f $todoman_configuration_file ]]; then
		return 0
	else
		return 1
	fi
}
# }}}
# {{{ general helper: set variable main.path from configuration file
__todo_set_conf_path(){
	if __todo_set_conf; then
		tasks_lists_path="$(sed -n -e 's/^[^#]\s*path\s*=\s*\(.*\)$/\1/p' $todoman_configuration_file 2>/dev/null)"
		# the eval echo is needed since the path may contain ~ which should be evalueated to $HOME
		tasks_lists_dir="$(eval echo ${tasks_lists_path%/\**})"
		if [[ -z "${tasks_lists_path}" || ! -d "${tasks_lists_dir}" ]]; then
			return 1
		else
			return 0
		fi
	else
		return 1
	fi
}
# }}}
# {{{ general helper: set variables related to date and time formats for __todo_date
__todo_set_conf_dt(){
	if __todo_set_conf; then
		date_format="$(eval echo $(sed -n -e 's/^[^#]\s*date_format\s*=\s*\(.*\)$/\1/p' $todoman_configuration_file 2>/dev/null))"
		dt_separator="$(eval echo $(sed -n -e 's/^[^#]\s*dt_separator\s*=\s*\(.*\)$/\1/p' $todoman_configuration_file 2>/dev/null))"
		time_format="$(eval echo $(sed -n -e 's/^[^#]\s*time_format\s*=\s*\(.*\)$/\1/p' $todoman_configuration_file 2>/dev/null))"
		# default value according to documentation: https://todoman.readthedocs.io/en/stable/configure.html
		if [[ -z "${date_format}" ]]; then
			date_format="%x"
		fi
		if [[ -z "${dt_separator}" ]]; then
			dt_separator=""
		fi
		if [[ -z "${time_format}" ]]; then
			time_format="%x"
		fi
		return 0
	else
		return 1
	fi
}
# }}}
# {{{ option helper: due and start date
__todo_date(){
	if __todo_set_conf_dt; then
		_message "date in format ${date_format//\%/%%}${dt_separator//\%/%%}${time_format//\%/%%}"
	else
		_message "date format (couldn't read configuration file and extract date and time formats)"
	fi
}
# }}}
# {{{ argument helper: sub-command choice
__todo_command(){
	local commands=(
		'cancel:Cancel one or more tasks'
		'copy:Copy tasks to another list'
		'delete:Delete tasks'
		'done:Mark one or more tasks as done'
		'edit:Edit the task with id ID'
		'flush:Delete done tasks'
		'list:List tasks'
		'move:Move tasks to another list'
		'new:Create a new task with SUMMARY'
		'show:Show details about a task'
	)
	_describe "command" commands
}
# }}}
# {{{ argument helper: available tasks choice
__todo_tasks(){
	# checking if the command jq exists and it's version
	# credit: http://stackoverflow.com/a/592649/4935114
	jq_version=$(jq --version 2>/dev/null)
	if [ ${${jq_version#jq\-}//./} -lt 15 ]; then
		_message "we can't complete tasks unless you'll install the latest version of jq: https://stedolan.github.io/jq/"
		return
	fi
	# $1 is a comma-seperated list of statuses to show when trying to complete this
	local status_search_query="$1"
	local -a tasks
	IFS=$'\n'
	for task_and_description in $(todo --porcelain list --status "${status_search_query}" | jq --raw-output '.[] | .id,":\"@",.list," ",.summary,"\"\\0"' | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n//g' -e 's/\\0/\n/g'); do
		tasks+="$(eval echo ${task_and_description})"
	done
	_describe tasks tasks
}
# }}}
# {{{ todo available lists cache policy
__todo_lists_cache_policy(){
	# the number of seconds since 1970-01-01 the directory
	local tasks_lists_dir_last_date_modified="$(date -r ${tasks_lists_dir} +%s 2>/dev/null)"
	# the number of seconds since 1970-01-01 the cache file was modified
	local cache_last_date_modified="$(date -r $1 +%s 2>/dev/null)"
	if [[ ! -z ${cache_last_date_modified} && ! -z ${tasks_lists_dir_last_date_modified} ]]; then
		# if the manifest file is newer then the cache:
		if [ ${tasks_lists_dir_last_date_modified} -ge ${cache_last_date_modified} ]; then
			(( 1 ))
		else
			(( 0 ))
		fi
	else
		(( 1 ))
	fi
}
# }}}
# {{{ option helper: available lists
__todo_lists(){
	if __todo_set_conf_path; then
		local update_policy
		zstyle -s ":completion:${curcontext}:" cache-policy update_policy
		if [[ -z "$update_policy" ]]; then
			zstyle ":completion:${curcontext}:" cache-policy __todo_lists_cache_policy
		fi
		local -a tasks_lists
		if _cache_invalid todoman_lists; then
			if [[ ${tasks_lists_path} =~ '/*$' ]]; then
				for dir in $(eval echo ${tasks_lists_path}); do
					if grep "VTODO" -q -R "${dir}"; then
						list_name="${dir##*/}"
						tasks_lists+=("${list_name}")
					fi
				done
			fi
			_store_cache todoman_lists tasks_lists
		else
			_retrieve_cache todoman_lists
		fi
		if [[ "${#tasks_lists[@]}" == 1 ]]; then
			_message "only one list was detected: (\"${tasks_lists[1]}\")"
			return
		else
			_describe "available lists" tasks_lists
			return
		fi
	else
		_message -e "no 'path = ' string was found in todoman's default configuration file ($todoman_configuration_file)"
		return
	fi
}
# }}}
# {{{ command `cancel`
_todo_cancel(){
	_arguments \
		"${common_options_help[@]}" \
		'*: :{__todo_tasks "IN-PROCESS,NEEDS-ACTION"}'
}
# }}}
# {{{ command `copy`
local _command_copy_options=(
	"${common_options_help[@]}"
	{-l,--list=}'[The list to copy the tasks to]:TEXT:__todo_lists'
)
_todo_copy(){
	_arguments \
		"${_command_copy_options[@]}" \
		'*: :{__todo_tasks "IN-PROCESS,NEEDS-ACTION"}'
}
# }}}
# {{{ command `delete`
local _command_delete_options=(
	"${common_options_help[@]}"
	"--yes[Don't ask for permission before deleting]"
)
_todo_delete(){
	_arguments \
		"${_command_delete_options[@]}" \
		'*: :{__todo_tasks "IN-PROCESS,NEEDS-ACTION"}'
}
# }}}
# {{{ command `done`
local _command_done_options=(
	"${common_options_help[@]}"
)
_todo_done(){
	_arguments \
		"${_command_done_options[@]}" \
		'*: :{__todo_tasks "IN-PROCESS,NEEDS-ACTION"}'
}
# }}}
# {{{ command `edit`
local _command_edit_options=(
	"${common_options_help[@]}"
	"${common_options_start[@]}"
	"${common_options_due[@]}"
	"${common_options_priority[@]}"
	"${common_options_location[@]}"
	"${common_options_interactive[@]}"
)
_todo_edit(){
	_arguments \
		"${_command_edit_options[@]}" \
		'*: :{__todo_tasks "IN-PROCESS,NEEDS-ACTION"}'
}
# }}}
# {{{ command `flush`
_todo_flush(){
}
# }}}
# {{{ command `list`
_command_list_options=(
	"${common_options_location[@]}"
	'--category=[Only show tasks with category containg TEXT]:TEXT:__todo_existing_categories'
	'--grep=[Only show tasks with message containg TEXT]:TEXT:'
	'--sort=[Sort tasks using these fields]:TEXT:(description location status summary uid rrule percent_complete priority sequence categories completed_at created_at dtstamp start due last_modified)'
	'(--reverse --no-reverse)'{--reverse,--no-reverse}'[sort tasks in reverse order (see --sort)]'
	"${common_options_start[@]}"
	"${common_options_due[@]}"
	'--priority[Only show tasks with priority at least as high as TEXT]:TEXT:("low", "medium", "high")'
	'--startable[Show only todos which should can be started today]'
	{-s,--status=}'[Show only todos with the provided comma-separated statuses]:STATUS:{_values -s , "status" "NEEDS-ACTION" "CANCELLED" "COMPLETED" "IN-PROCESS" "ANY"}'
	"${common_options_help[@]}"
)
_todo_list(){
	_arguments \
		"${_command_list_options[@]}" \
		'1: :__todo_lists' \
}
# }}}
# {{{ command `move`
_todo_move(){
	_todo_copy
}
# }}}
# {{{ command `new`
local _command_new_options=(
	"${common_options_start[@]}"
	"${common_options_due[@]}"
	"${common_options_help[@]}"
	{-l,--list=}'[The list to move the tasks to]:TEXT:__todo_lists'
	'--location[The location where this todo takes place.]:TEXT:__todo_existing_locations'
	"${common_options_priority[@]}"
	"${common_options_interactive[@]}"
)
_todo_new(){
	_arguments \
		"${_command_new_options[@]}" \
		'*: :{_message "summary"}'
}
# }}}
# {{{ command `show`
_todo_show(){
	_todo_done
}
# }}}

# The real thing
_arguments -C -A "-*" \
	{-v,--verbosity=}'[Set verbosity to the given level]:MODE(CRITICAL ERROR WARNING INFO DEBUG)' \
	'--color=[Set colored output mode]:MODE:__color_mode' \
	'--porcelain[Use a JSON format that will remain stable regadless of configuration or version]' \
	{-h,--humanize}'[Format all dates and times in a human friendly way]' \
	'(- :)--version[Show the version and exit]' \
	"${common_options_help[@]}" \
	'1: :__todo_command' \
	'*::arg:->args'

case $state in
	(args)
		curcontext="${curcontext%:*:*}:todo_$words[1]:"
		case "${words[1]}" in
			cancel)
				_todo_cancel
				;;
			copy)
				_todo_copy
				;;
			delete)
				_todo_delete
				;;
			done)
				_todo_done
				;;
			edit)
				_todo_edit
				;;
			flush)
				_todo_flush
				;;
			list)
				_todo_list
				;;
			move)
				_todo_move
				;;
			new)
				_todo_new
				;;
			show)
				_todo_show
				;;
		esac
		;;
esac