#compdef pdfjam

_pdfjam() {
	local paperformats=(a0 a1 a2 a3 a4 a5 a6 b0 b1 b2 b3 b4 b5 b6 c0 c1 c2 c3 c4 c5 c6 ansia ansib ansic ansid ansie letter legal executive b0j b1j b2j b3j b4j b5j b6j)
	local paperflags=(
		--${^paperformats:#b?j}paper
		--${(M)^paperformats:#b?j}
	)
	local options=(
		# Change operation mode
		'(- :)'{-h,--help}'[Print help message.]'
		'(- :)'{-V,--version}'[Print the version number.]'
		'(- :)--configpath[Print the `configpath` variable.]'
		{-q,--quiet}'[Suppress verbose commentary on progress.]'
		'--batch[Run pdfjam sequentially on each input file in turn, and produce a separate output file for each input.]'
		'(--no-checkfiles)--checkfiles[Use `file` utility to determine file type.]'
		'(--checkfiles)--no-checkfiles[Do not use `file` utility to determine file type.]'
		'--vanilla[Suppress the reading of any pdfjam configuration files.]'
		'--enc[Command-line encoding as understood by `iconv`.]:encoding:_enc'
		# Define where to put files
		'(- :)'{-o,--outfile,--output}'[File or directory name for output(s).]:output:_output'
		'--suffix[Suffix for output, when file name is not given explicitly.]:string: '
		'(--builddir --no-tidy)--tidy[Clean temporary build directory. On by default.]'
		'(--builddir --tidy)--no-tidy[Do not clean temporary build directory. On by default.]'
		'(--tidy --no-tidy)--builddir[Set build directory.]:directory:_files -/'
		# Configure LaTeX run
		'--latex[Absolute path to LaTeX engine to be used.]:engine:_absolute_command_paths'
		'*--latexopts[Provide arguments to use when calling the LaTeX engine.]:latex command line options: '
		'*--preamble[Append code to the LaTeX preamble.]:tex: '
		'--runs[Run latex N times, for each output document made.]:number of runs:_numbers -l 1 "number of runs"'
		# Set PDF metadata
		'(--no-keepinfo)--keepinfo[Preserve Title, Author, Subject and Keywords (from the last input PDF) in the output PDF file.]'
		'(--keepinfo)--no-keepinfo[Do not preserve Title, Author, Subject and Keywords (from the last input PDF) in the output PDF file.]'
		'--pdftitle[Set Title of the output PDF file.]:string: '
		'--pdfauthor[Set Author of the output PDF file.]:string: '
		'--pdfsubject[Set Subject of the output PDF file.]:string: '
		'--pdfkeywords[Set Keywords of the output PDF file.]:string: '
		# Adjust page
		'(--no-landscape)--landscape[Exchange width and height of paper.]'
		'(--landscape)--no-landscape[Do not exchange width and height of paper.]'
		'(--no-twoside)--twoside[Specify `twoside` document class option.]'
		'(--twoside)--no-twoside[Do not specify `twoside` document class option.]'
		'(--no-otheredge)--otheredge[Rotate every odd page by 180 degrees.]'
		'(--otheredge)--no-otheredge[Do not rotate every odd page by 180 degrees.]'
		'--pagecolor[Background color.]:rgb:_pagecolor'
		# Pdfpages settings
		## N-up pages
		'--nup[Put multiple logical pages onto each sheet of paper.]:XxY:_nup'
		'--column[Use column-major layout, where successive pages are arranged in columns down the paper.]:bool:(true false)'
		'--columnstrict[For column-major layout only: Do not balance the columns on the last page.]:bool:(true false)'
		## Shift pages
		'--delta[Put some horizontal and vertical space between the logical pages.]:horizontal-space vertical-space:_dimens " " horizontal-space,vertical-space'
		'--offset[Displace the origin of the inserted pages.]:horizontal-displacement vertical-displacement:_dimens " " horizontal-displacement,vertical-displacement'
		'--frame[Put a frame around each logical page.]:bool:(true false)'
		## Scale pages
		'--noautoscale[Suppress automatic scaling of pages.]:bool:(true false)'
		'--fitpaper[Adjust the paper size to the size of the inserted document.]:bool:(true false)'
		'--turn[Tell PDF viewer to display landscape pages in landscape orientation. On by default.]:bool:(true false)'
		'--pagetemplate[Declare page to be used as a template. All other pages are scaled such that they match within its size.]:pagenumber:_numbers -l 1 "pagenumber"'
		'--templatesize[Specify size of page template. All pages are scaled such that they match within this size.]:width,height:_dimens , width,height'
		'--rotateoversize[Rotate oversized pages.]:bool:(true false)'
		## Mirror pages
		'--reflect[Reflect output pages.]:bool:(true false)'
		'--reflect\*[Reflect input pages.]:bool:(true false)'
		'--flip-other-edge[For signatures/booklets: set duplex binding edge perpendicular (instead of parallel) to signature binding edge. Has same result as `otheredge`.]:bool:(true false)'
		## Add blank pages
		'--openright[Put an empty page before the very first logical page.]:bool:(true false)'
		'--openrighteach[Put an empty page before the first logical page of each file.]:bool:(true false)'
		## Run LaTeX commands
		'--pagecommand[Declare LaTeX commands, which are executed on each sheet of paper. Default: `\\thispagestyle{empty}`.]:tex: '
		'--pagecommand\*[Declare LaTeX commands, which are executed on the very first page only.]:tex: '
		'--picturecommand[Similar to pagecommand, but executed within a picture environment with base point at the lower left page corner.]:tex: '
		'--picturecommand\*[Similar to picturecommand, but for very first page only.]:tex: '
		## Add hyperlinks
		'--link[Each inserted page becomes the target of the hyperlink ⟨filename⟩.⟨pagenumber⟩.]:bool:(true false)'
		'--linkname[For link option only: Change the link base name from ⟨filname⟩ to name.]:name: '
		'--threadname[For thread option only: Change the thread name from ⟨filename⟩ to name.]:name: '
		'--linkfit[For link option only: Specify, how the viewer displays a linked page.]:destination:_linkfit'
		'--addtotoc[Add an entry to the table of contents.]:pagenumber,section,level,heading,label:_addtotoc'
		'--addtolist[Add an entry to the list of figures, the list of tables, or any other list.]:pagenumber,type,heading,label:_addtolist'
		# Graphicx options
		'--viewport[Consider image to consist of given rectangle only.]:left bottom right top:_dimens " " left,bottom,right,top'
		'--trim[Similar to viewport, but here the four lengths specify the amount to remove or add to each side.]:left bottom right top:_dimens " " left,bottom,right,top'
		'--angle[Rotation angle (counterclockwise).]:angle:compadd -o nosort $(seq 0 15 345)'
		'--width[Required width. The graphic is scaled to this width.]:width:_dimen width'
		'--height[Required height. The graphic is scaled to this height.]:height:_dimen height'
		'--keepaspectratio[Do not distort figure if both width and height are given.]:bool:(true false)'
		'--scale[Scale factor. Applied after automatic scaling.]:float: '
		'--clip[Clip the graphic to the viewport.]:bool:(true false)'
		'--pagebox[Specify which PDF bounding box specification to read.]:bounding box:(mediabox cropbox bleedbox trimbox artbox)'
		'--draft[Switch to draft mode.]:bool:(true false)'
		# Completion groups
		# Set paper format
		+ '(paper)'
			# Set paper format
			"--paper[Paper format.]:paper name:($paperformats)"
			'--papersize[Specify a paper size as width × height.]:width,height:_dimens , width,height'
			$paperflags
		## Create signatures
		+ '(signature)'
			## Create signatures
			'--signature[Create booklets by rearranging pages into signatures of N (the argument) pages each.]:signature size:compadd -o nosort $(seq 4 4 96)'
			'--signature\*[Similar to signature, but for right-edge binding.]:signature size:compadd -o nosort $(seq 4 4 96)'
			'--booklet[Same as signature with signature size chosen such that all pages fit into one signature.]:bool:(true false)'
			'--booklet\*[Similar to booklet, but for right-edge binding.]:bool:(true false)'
		## Duplicate pages
		+ '(doublepages)'
			## Duplicate pages
			'--doublepages[Insert every page twice.]:bool:(true false)'
			'--doublepagestwist[Insert every page twice: once upside down and once normally.]:bool:(true false)'
			'--doublepagestwistodd[Insert every page twice: once normally and once upside down.]:bool:(true false)'
			'--doublepagestwist\*[Insert every page twice: for odd pages, the first copy is upside down; for even pages, the second copy.]:bool:(true false)'
			'--doublepagestwistodd\*[Insert every page twice: for odd pages, the second copy is upside down; for even pages, the first copy.]:bool:(true false)'
			'--duplicatepages[Insert every page multiple times.]:number:_numbers -l 1 "number"'
	)

	_file_or_pagespec() {
		_files -g '*.(pdf|ps|eps|jpg|png)'
		# If the previous word is a pdf or ps file and a normal argument then it may be a pagespec.
		[[ "${(L)words[$CURRENT-1]}" = *.(pdf|ps) ]] && _normal_argument_follows "$words[1,$CURRENT-2]" && _pagespec
	}

	_arguments -S '*:file or pagespec:_file_or_pagespec' $options
}

_normal_argument_follows() {
	while shift 2>/dev/null; do
		if [[ "$1" = -- ]]; then
			return
		elif _option_expects_argument "$1"; then
			shift 2>/dev/null || return
		fi
	done
}

_option_expects_argument() {
	[[ "$1" = -* ]] &&
	! [[ "${(M)#flags:#"$1"\[*}" = 1 ]] &&
	! [[ "${(M)#paperflags:#"$1"}" = 1 ]]
}

_pagespec() {
	compset -P '*,'
	if compset -P 1 '*-'; then
		_numbers -l 1
		if ! compset -P '[0-9]##|last'; then
			_description -x last expl 'last page' && compadd -S, -r ' \t\n\-' "$expl[@]" last
		fi
		_description -x comma expl 'comma' && compadd -R _remove_comma -S '' "$expl[@]" ,
	else
		_numbers -l 1
		if compset -P '[0-9]##|last'; then
			_description -x range expl 'range' && compadd -S '' "$expl[@]" - -
			_description -x comma expl 'comma' && compadd -R _remove_comma "$expl[@]" ,
		else
			_description -x range expl 'range from 1' && compadd -S '' "$expl[@]" - -
			_description -x empty expl 'empty page' && compadd -Q -qS, "$expl[@]" {}
			_description -x last expl 'last page' && compadd -S '' "$expl[@]" last
			# _description -x none expl 'implicit empty page' && compadd -S '' "$expl[@]" , # valid but not recommended
		fi
	fi
}

_remove_comma() { # Remove comma which was likely added only accidentally. NB: Would add an empty page otherwise.
	case "$KEYS" in
		' '|$'\n'|$'\t'|$'\r') LBUFFER=${LBUFFER%,} ;;
	esac
}

_output() {
	_alternative 'file:pdf:_files -g \*.pdf' 'directory:path:_files -/'
}

_dimen() {
	local units=(
		':bp:point (1/72 in)'
		'pt:point (1/72.27 in)'
		mm:millimeter
		cm:centimeter
		'in:inch (2.54 cm)'
		'sp:scaled point (1/65536 pt)'
		'pc:pica (12 pt)'
		'dd:Didot point (1.07 pt)'
		'cc:Cicero (12 dd)'
		'ex:x-height (height of lowercase x)'
		'em:font size (height of capital M)'
	)
	_numbers -f -u bp "$@" $units
}

_dimens() {
	local sep="$argv[-2]"
	typeset -a names=("${(s:,:)argv[-1]}")
	typeset -i i=$#names
	shift -p 2
	if [[ "$QIPREFIX" = (\'|\") ]]; then
		close="$QIPREFIX"
	else
		close=
		[[ "$sep" = ' ' ]] && sep="\\$sep"
	fi
	while ((--i)); do compset -P $i "*$sep" && break; done
	((++i))
	[[ $i = $#names ]] && sep="$close "
	_dimen "$@" -S "$sep" "$names[$i]"
}

_nup() {
	setopt extendedglob
	case "$PREFIX" in
		[0-9]##x[0-9]#) compset -P '*x'; _numbers -l 1 "$@" vertical ;;
		[0-9]##) compset -P '*'; _numbers -l 1 "$@" horizontal; compadd "$@" -S '' x ;;
		[0-9]#) _numbers -l 1 "$@" horizontal ;;
		*) return 1 ;;
	esac
}

_pagecolor() {
	if compset -P 3 '*,'; then return 1
	elif compset -P 2 '*,'; then
		_numbers -l 0 -m 255 "$@" blue
	elif compset -P 1 '*,'; then
		_numbers -l 0 -m 255 "$@" green
	else
		_numbers -l 0 -m 255 "$@" red
	fi
}

_linkfit() {
	local expl tag
	if compset -P 1 '*\ '; then
		_numbers 'distance in points (1/72 in)'
	else
		compadd "$@" Fit FitB Region
		compadd -S '\ ' "$@" FotV FotBH FitBV
	fi
}

_addtotoc() { # {page number, section, level, heading, label}
	local close expl
	if compset -P '*\{'; then close='\}'
	elif compset -P '*{'; then close='}'
	fi
	if ! compset -P 1 '*,'; then
		compset -P '*'
		_description -x string expl 'page number' && compadd "$expl[@]"
		_description -x separator expl comma && compadd "$expl[@]" ,
	elif ! compset -P 2 '*,'; then
		_alternative 'level:level:(part,0, section,1, subsection,2, subsubsection,3,)'
	elif ! compset -P 1 '*,'; then
		compset -P '*'
		_description -x string expl heading && compadd "$expl[@]"
		_description -x separator expl comma && compadd "$expl[@]" ,
	elif ! compset -P 1 '*,' && ! compset -P "*$close"; then
		compset -P '*'
		_description -x string expl 'LaTeX label' && compadd "$expl[@]"
		[[ -n "$close" ]] && _description -x delimiter expl brace && compadd -Q "$expl[@]" - "$close"
	fi
}

_addtolist() { # {page number, type, heading, label}
	local close
	if compset -P '*\{'; then close='\}'
	elif compset -P '*{'; then close='}'
	fi
	if ! compset -P 1 '*,'; then
		compset -P '*'
		_description -x string expl 'page number' && compadd "$expl[@]"
		_description -x separator expl comma && compadd "$expl[@]" ,
	elif ! compset -P 1 '*,'; then
		_alternative 'type:type:(figure, table,)'
	elif ! compset -P 1 '*,'; then
		compset -P '*'
		_description -x string expl heading && compadd "$expl[@]"
		_description -x separator expl comma && compadd "$expl[@]" ,
	elif ! compset -P 1 '*,' && ! compset -P "*$close"; then
		compset -P '*'
		_description -x string expl 'LaTeX label' && compadd "$expl[@]"
		[[ -n "$close" ]] && _description -x delimiter expl brace && compadd -Q "$expl[@]" - "$close"
	fi
}

_enc() {
	local enc=$(iconv -l)
	enc=${(@q)${(ou)${${(f)enc}%//}}}
	_alternative "encoding:code set:($enc)"
}

if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
	_pdfjam "$@"
else
	compdef _pdfjam pdfjam
fi
