# Rename dirs to `snake_case` and files to `PascalCase`. Careful with structured file names like archives!
# Usage: name [FILES]
function name() {
	local IFS=$'\n'
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=($(ls))

	process() {
		# Skip archive.
		if $(_is_archive "${target}"); then
			_iterate_skip "File is an archive, skip."
			return 0
		fi

		if [[ -d ${target} ]]; then
			local new_name=$(parse_snake ${target})
			[[ -e ${new_name} ]] && return 0

			mv -- ${target} ${new_name} && echo ${new_name}
		else
			local ext=".${target##*.}"
			local name=${target%.*}
			[[ ${ext} == ".${target}" ]] && ext=""

			local new_name="$(parse_pascal ${name})${ext}"
			[[ -e ${new_name} ]] && return 0

			mv -- ${target} ${new_name} && echo ${new_name}
		fi
	}

	_iterate_targets process ${targets[@]}
}

# Rename files with provided parser, i.e. `parse_simple`.
# All files by default.
# Usage: name_parse <PARSER> [FILES]
function name_parse() {
	local IFS=$'\n'
	local parser=${1}
	local targets=(${@:2})
	[[ ${targets} == "" ]] && targets=("[^.]*")

	if [[ ${parser} == "" ]]; then
		help name_parse
		return 2
	fi

	process() {
		# Skip archive.
		if $(_is_archive "${target}"); then
			_iterate_skip "File is an archive, skip."
			return 0
		fi

		# parse new name.
		local ext=""
		local name="${target}"

		# ext only for files.
		if [[ -f ${target} ]]; then
			ext=".${target##*.}"
			name="${target%.*}"
		fi

		# Files w/o extension support.
		[[ ${ext#.} == "${name}" ]] && ext=""

		# Get new name.
		local new_name=$(${parser} "${name}")${ext,,}

		# check if same name.
		[[ ${target} == "${new_name}" ]] && return 0

		# check if target name already exists.
		if [[ -f ${new_name} ]]; then
			_error "${new_name}: Already exists!"
			return 1
		fi

		# rename target.
		mv -- "${target}" "${new_name}" && echo "${new_name}"
	}

	_iterate_targets process ${targets[@]}
}

# Rename all files to their hashes while keeping extensions.
# All files by default.
# Usage: name_hash [FILES]
function name_hash() {
	local IFS=$'\n'
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=($(_ls_file))

	process() {
		# extract extension.
		local extension="${target##*.}"
		if [[ ${extension} == "${target}" ]]; then
			extension=""
		else
			extension=".${extension}"
		fi

		# hash the new name.
		local hash=$(pv "${target}" | sha1sum | cut -d\  -f1)
		new_name="${hash,,}${extension,,}"

		# check if same name.
		[[ ${target} == "${new_name}" ]] && return 0

		# rename target.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Check hashes for previously renamed files.
# All files by default.
# Usage: name_hash_check [FILES]
function name_hash_check() {
	local IFS=$'\n'
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=("[^.]*")

	process() {
		# extract hashes.
		local stored="${target%%.*}"
		local actual=$(pv "${target}" | sha1sum | cut -d\  -f1)

		# compare hashes.
		if [[ ${stored} != "${actual}" ]]; then
			_error "Failed."
			return 1
		fi
	}

	_iterate_targets process ${targets[@]}
}

# Rename files for Jellyfin shows, i.e. `Episode S01E01.mkv`
# All files by default.
# Usage: name_show [FILES]
function name_show() {
	local IFS=$'\n'
	local season="$(realpath .)"
	season="${season##*\ }"
	local episode=0
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=($(_ls_file))

	# Error when no season number specified.
	if [[ ${season} == "" ]]; then
		_error "Could not determine season number."
		return 2
	fi

	process() {
		((episode++))

		# extract new name.
		local new_name="Episode S${season}E$(printf %02d ${episode}).${target##*.}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# rename target.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Rename files for Kavita manga format.
# All files by default.
# Usage: name_manga <SEASON> [FILES]
function name_manga() {
	local IFS=$'\n'
	local manga=${PWD##*/}
	local season=${1}
	local episode=0
	local targets=(${@:2})
	[[ ${targets} == "" ]] && targets=($(_ls_file))

	# Error when no season number specified.
	if [[ ${season} == "" ]]; then
		help name_manga
		return 2
	fi

	process() {
		((episode++))

		# Extract new name.
		local new_name="${manga} Vol.${season} Ch.${episode}.${target##*.}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# Rename target.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Rename files for ffmpeg_music_meta format.
# All files by default.
# Usage: name_music [FILES]
function name_music() {
	local IFS=$'\n'
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=($(ls))

	process() {
		# Extract new name.
		local ext=${target##*.}

		if [[ -d ${target} ]]; then
			local new_name="$(parse_startcase $(parse_simple ${target%.*}))"
		else
			local new_name="$(parse_startcase $(parse_simple ${target%.*})).${ext}"
		fi

		# Skip on no change.
		[[ ${target%/} == "${new_name}" ]] && return 0

		# Rename target.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Rename files with new extension.
# All files by default.
# Usage: name_ext <EXTENSION> [FILES]
function name_ext() {
	local IFS=$'\n'
	local extension=${1}
	local targets=(${@:2})
	[[ ${targets} == "" ]] && targets=($(_ls_file))

	# Error when no new extension specified.
	if [[ ${extension} == "" ]]; then
		help name_ext
		return 2
	fi

	process() {
		# Extract new name.
		local new_name="${target%.*}"."${extension}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# Rename target.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Change file name prefix.
# All matching files by default.
# Usage: name_prefix <OLD> <NEW> [FILES]
function name_prefix() {
	local IFS=$'\n'
	local old=${1}
	local new=${2}
	local targets=(${@:3})
	[[ ${targets} == "" ]] && targets=(${old}*)

	process() {
		# Create new name.
		local new_name="${new}${target#$old}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# Rename.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Change file name postfix.
# All matching files by default.
# Usage: name_postfix <OLD> <NEW> [FILES]
function name_postfix() {
	local IFS=$'\n'
	local old=${1}
	local new=${2}
	local targets=(${@:3})
	[[ ${targets} == "" ]] && targets=(*${old})

	process() {
		# Create new name.
		local new_name="${target%$old}${new}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# Rename.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Replace part of the name.
# All matching files by default.
# Usage: name_replace <OLD> <NEW> [FILES]
function name_replace() {
	local IFS=$'\n'
	local old=${1}
	local new=${2}
	local targets=(${@:3})
	[[ ${targets} == "" ]] && targets=(*${old}*)

	process() {
		# Create new name.
		local new_name="${target//$old/$new}"

		# Skip on no change.
		[[ ${target} == "${new_name}" ]] && return 0

		# Rename.
		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

# Fix numbering for numbered files. I.e if there are 10 items and some of them start without zero, then append zero to it. 1..10 -> 01..10.
# Usage: name_fix_numbering [FILES]
function name_fix_numbering() {
	local IFS=$'\n'
	local highest=0
	local power=0
	local targets=(${@})
	[[ ${targets} == "" ]] && targets=($(ls | grep "^[0-9]"))

	# Count leading zeroes.
	for target in "${targets[@]}"; do
		# Check that starts with a digit.
		[[ ${target} =~ ^[0-9] ]] || continue

		local digits=($(parse_ints "${target}"))
		local digit="${digits[0]}"
		digit=$((10#${digit}))

		[[ ${digit} -gt ${highest} ]] && highest="${digit}"
	done

	local i=${highest}
	while [[ i -gt 0 ]]; do
		((power++))
		i=$((i / 10))
	done

	process() {
		# Check that starts with a digit.
		if [[ ! ${target} =~ ^[0-9] ]]; then
			_error "Does not start with a digit!"
			return 1
		fi

		# Prepare new name.
		local digits=($(parse_ints "${target}"))
		local digit="${digits[0]}"
		digit=$((10#${digit}))
		local new_name=$(printf "%0${power}d" "${digit}")"${target#${digits[0]}}"

		# Skip if the same name.
		[[ ${target} == "${new_name}" ]] && return 0

		# Check that file does not exist.
		if [[ -e ${new_name} ]]; then
			_error "${new_name}: File exists!"
			return 1
		fi

		mv -- ${target} ${new_name} && echo ${new_name}
	}

	_iterate_targets process ${targets[@]}
}

function _comp_name_parse() {
	_autocomplete $(find_function | grep ^parse)
}

complete -o filenames -F _comp_name_parse name_parse