#!/usr/bin/env ksh
#
# Copyright (c) 2018-2021 Jordan Geoghegan <jordan@geoghegan.ca>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

# Version 0.1-BETA "Tears in the Rain" -- Released TBD 00, 2021

# Process audio data to give buttery smooth night mode quality.

version='0.1-BETA_1'
release_date='TBD'
release_name='Tears in the Rain'

set -efo pipefail

# ------------------------------------------------------------------------------
# Abort Sequences and Housekeeping
# ------------------------------------------------------------------------------

ERR() {
	typeset sanitized
	sanitized="$(printf '%s' "$*" | tr '[:cntrl:]' '?')"
	WARN_ERR "$sanitized"
	exit 1
}

WARN_ERR() {
	printf 'ERROR: %s\n' "$*" 1>&2
}


CHECK_CMD() {
	typeset _cmd
	for _cmd in "$@"; do
		command -v -- "${_cmd}" || ERR "Command not found: '${_cmd}'"
	done  >/dev/null
}

CHECK_DIR() {
	typeset _dir
	for _dir in "$@"; do
		[[ -d ${_dir} ]] || ERR "Directory not found: '${_dir}'"
		[[ -w ${_dir} ]] || ERR "Write permission denied: '${_dir}'"
	done  
}

CHECK_R_PERM(){
	typeset _file
	for _file in "$@"; do
		[[ -f ${_file} ]] || ERR "File not found: '${_file}'"
		[[ -r ${_file} ]] || ERR "Read permission denied: '${_file}'"
	done
}

IS_BOOL() {
	case "$*" in
	    0|1) return 0 ;;
	    *) return 1 ;;
	esac
}

IS_INT() {
	case "$*" in
	    ''|*[!0-9]*) return 1 ;;
	    *) return 0 ;;
	esac
}


# ------------------------------------------------------------------------------
# Main Function
# ------------------------------------------------------------------------------

main() {
	# Initialize default variables
	aBitrate='160k'
	aCodec='libopus'
	vCodec='copy'

	# ffmpeg vars
	aDownmix51='pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR'
	aNormalize='loudnorm=I=-16:LRA=11:TP=-1.5'
	vContainer="mkv"
	oDir="./nightshift-cnv"

	# Initialize command line flag vars
	typeset -l _opt_arg

	# Command-line option handling
	while getopts A:B:C:D:V:o: _opts ; do
	case "${_opts}" in
	    A)  # Audio codec
		case "${OPTARG}" in
		    aac|ac3|libopus|libvorbis) aCodec="${OPTARG}" ;;
		    *) ERR "-A: Invalid argument: '${OPTARG}'" ;;
		esac
		((++_Aflag == 1)) || ERR '-A: Options are mutually exclusive' ;;
	    B)  # Audio bitrate
		if [[ ${OPTARG} == +([[:digit:]])k ]]; then
			aBitrate="${OPTARG}"
		else
			ERR "-B: Invalid argument: '${OPTARG}'"
		fi
		((++_Bflag == 1)) || ERR '-B: Options are mutually exclusive' ;;
	    C)  # Container format
		case "${OPTARG}" in
		    m2ts|mkv|mp4) vContainer="${OPTARG}" ;;
		    *) ERR "-C: Invalid argument: '${OPTARG}'" ;;
		esac
		((++_Cflag == 1)) || ERR '-C: Options are mutually exclusive' ;;
	    D)  # Output directory
		CHECK_DIR "${OPTARG}"
		((++_Dflag == 1)) || ERR '-D: Options are mutually exclusive' ;;
	    V)  # Audio codec
		case "${OPTARG}" in
		    copy|libx264) vCodec="${OPTARG}" ;;
		    *) ERR "-V: Invalid argument: '${OPTARG}'" ;;
		esac
		((++_Vflag == 1)) || ERR '-V: Options are mutually exclusive' ;;
	    o)  # Long form runtime options
		_opt_arg="${OPTARG}"
		case "${_opt_arg}" in
		    downmix|stereo) _DOWNMIX=1 ;;
		    ebu-r128|normalize) _NORMALIZE=1 ;;
		    no-downmix|no-stereo) _DOWNMIX=0 ;;
		    no-ebu-r128|no-normalize) _NORMALIZE=0 ;;		    
		    *) ERR "-o: Invalid argument: '${OPTARG}'" ;;
		esac ;;
	    :)  ERR "-${OPTARG}: requires argument" ;;
	    ?)  ERR "-${OPTARG}: unknown option" ;;
    	esac
	done
	shift $((OPTIND - 1))

	# Make sure video files are accessible
	CHECK_R_PERM "$@"

	if [[ -e "${oDir}" ]]; then
		CHECK_DIR "${oDir}"
	else
		mkdir -p "${oDir}" || ERR 'Failed to create output directory!'
		echo "Outputing converted files to: '${oDir}'"
		sleep 3
	fi

	# Set audio filter parameters
	if ((_DOWNMIX == 1 && _NORMALIZE == 1)); then
		aFilter="${aDownmix51},${aNormalize}"
	elif ((_DOWNMIX == 1 && _NORMALIZE == 0)); then
		aFilter="${aDownmix51}"
	else
		aFilter="${aNormalize}"
	fi

	for _i in "$@" ; do
		extStrip="${_i%.*}"
		baseFile="${extStrip##*/}"
		oLoc="${oDir}/${baseFile}.${vContainer}"
		[[ -e ${oLoc} ]] && { echo "Overwriting file: '${oLoc}'" ; sleep 3 ;} 
		nice -n 20 -- ffmpeg -loglevel error -stats -y -i "${_i}" -c:v "${vCodec}" -c:a "${aCodec}" -b:a "${aBitrate}" -af "${aFilter}" -- "${oLoc}"
	done
}

main "$@"
