#!/usr/bin/env bash

# Version: 3.0.0

# If ffmpeg_executable is not set, use ffmpeg
if [[ -z $ffmpeg_executable ]]; then
	# If have jellyfin-ffmpeg installed, use it
	if [[ -e /usr/lib/jellyfin-ffmpeg/ffmpeg ]]; then
		ffmpeg_executable="/usr/lib/jellyfin-ffmpeg/ffmpeg"
	else
		ffmpeg_executable="ffmpeg"
	fi
fi

# If show_executable is set, show the ffmpeg executable path
if [[ $show_executable == 1 ]]; then
	echo "$ffmpeg_executable"
	exit 0
fi

# If vainfo is available, check for VAAPI encoders before ffmpeg to faster result
# Only check if not using software encoding
if [[ "$gpu" != "software" && "$force_software" != "1" ]]; then
	if ! command -v vainfo >/dev/null; then
		echo "Command vainfo not found"
		exit 1
	fi

	# If vainfo is available, check for VAAPI encoders before ffmpeg to faster result
	if ! command -v lspci >/dev/null; then
		echo "Command lspci not found"
		exit 1
	fi
fi

# Define color variables
COLOR_RESET="\e[0m"
COLOR_GREEN="\e[32m"
COLOR_YELLOW="\e[33m"
COLOR_CYAN="\e[36m"
COLOR_BLUE="\e[34m"

# Show help if no arguments are passed or the first argument is -h or --help
if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]]; then
	echo -e "${COLOR_CYAN}Usage:${COLOR_RESET} ${COLOR_YELLOW}variables=value${COLOR_RESET} $0 ${COLOR_YELLOW}input_file${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_CYAN}You can specify various variables before the command. Below are the available options and examples:${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}1. Specifying the GPU:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify which GPU to use for the operation. Options include:${COLOR_GREEN} auto, nvidia, amd, intel, software${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}gpu=nvidia${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}2. Specifying the Output File:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the name of the output file. If this variable is not specified, a default name will be used.${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}output_file=video_converted.mp4${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}3. Specifying the Output Folder:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the folder where the output file will be saved. This will save the file with the same name as the input file in the specified folder.${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}If ${COLOR_YELLOW}output_file${COLOR_CYAN} is specified, this variable is ignored.${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}output_folder=/home/user/Videos${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}4. Specifying Video Quality:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the quality of the video. Options include:${COLOR_GREEN} veryhigh, high, medium, low, verylow${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}video_quality=high${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}5. Specifying Audio Bitrate:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the bitrate of the audio. Example:${COLOR_GREEN} 128k, 192k, 256k${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Default is 32k per channel, stereo audio will have 64k bitrate, and 5.1 audio will have 192k bitrate${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}audio_bitrate=192k${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}6. Specifying Audio Channels:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the number of audio channels. If not specified, the default will be used.${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}audio_channels=2${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}7. Video Filter:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the video filter for ffmpeg. Example:${COLOR_GREEN} -vf scale=1280x720${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}8. Specifying Compression Preset:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the preset for compression.:${COLOR_GREEN} ultrafast, veryfast, faster, medium, slow, veryslow${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Default is medium for GPU encode and faster for software encode${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example:${COLOR_RESET} ${COLOR_YELLOW}preset=veryslow${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}9. Passing Additional Options to FFmpeg:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can pass additional options directly to FFmpeg using the ${COLOR_YELLOW}options${COLOR_CYAN} variable.${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}Example to cut a video from 1 minute to 30 seconds:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}options=\"-ss 60 -t 30\"${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}10. Specifying Subtitle Handling:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify how to handle subtitles. Options include:${COLOR_GREEN} extract, embedded, none${COLOR_RESET} Default is extract"
	echo -e "   ${COLOR_CYAN}Example to extract subtitles:${COLOR_RESET} ${COLOR_YELLOW}subtitle_extract=extract${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}11. Specifying Audio Handling:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify how to handle audio. Options include:${COLOR_GREEN} copy, reencode, none${COLOR_RESET} Default is copy"
	echo -e "   ${COLOR_CYAN}Example to copy audio without reencoding:${COLOR_RESET} ${COLOR_YELLOW}audio_handling=copy${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}12. Specifying Audio Codec for Re-encoding:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the audio codec when re-encoding. Options include:${COLOR_GREEN} aac, opus, ac3${COLOR_RESET} Default is aac"
	echo -e "   ${COLOR_CYAN}Example to use opus codec:${COLOR_RESET} ${COLOR_YELLOW}audio_codec=opus${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}13. Specifying Video Encoder:${COLOR_RESET}"
	echo -e "   ${COLOR_CYAN}You can specify the video encoder. Options include:${COLOR_GREEN} h264, h265, av1, vp9${COLOR_RESET} Default is h264"
	echo -e "   ${COLOR_CYAN}Example to use h265 encoder:${COLOR_RESET} ${COLOR_YELLOW}video_encoder=h265${COLOR_RESET} $0 ${COLOR_YELLOW}video.mkv${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}14. Force Gpu partial mode, decode using CPU and encode using GPU:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}gpu_partial=1${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}15. Force CPU decode and encode:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}force_software=1${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}16. Copy video without reencode:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}force_copy_video=1${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}17. Only extract subtitles to .srt files:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}only_extract_subtitles=1${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}18. Show ffmpeg executable path:${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}show_executable=1${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}19. Force ffmpeg encoder. Options: nvenc, vulkan, vaapi, qsv, amf, software ${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}force_encoder=encoder_name${COLOR_RESET}"
	echo ""
	echo -e "${COLOR_BLUE}20. Force ffmpeg decoder. Options: cuda, vaapi, qsv, amf, software ${COLOR_RESET}"
	echo -e "   ${COLOR_YELLOW}force_decoder=decoder_name${COLOR_RESET}"
	exit 0
fi

# Use the first argument as the input file
input_file="$1"

# Detect bit depth and codec of input video
echo "Iniciando o processo FFmpeg..."
echo "Analyzing input video..."
pix_fmt=$(ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of csv=p=0 "$1" 2>/dev/null)
video_profile=$(ffprobe -v error -select_streams v:0 -show_entries stream=profile -of csv=p=0 "$1" 2>/dev/null)

# Determine if video is 10-bit based on pixel format and profile
is_10bit=false
if [[ $pix_fmt =~ yuv.*p10 || $pix_fmt =~ yuv.*10le || $pix_fmt =~ .*10bit.* ]]; then
	is_10bit=true
	echo "Detected 10-bit video input (pixel format: $pix_fmt)"
elif [[ $video_profile =~ .*10.* || $video_profile =~ .*High\ 10.* || $video_profile =~ .*Main\ 10.* ]]; then
	is_10bit=true
	echo "Detected 10-bit video input (profile: $video_profile)"
else
	echo "ℹ️  Detected 8-bit video ($pix_fmt) - using standard profile"
fi

echo "Detecting source video codec..."
source_video_codec=$(ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "$input_file" 2>/dev/null | head -n1)
echo "Source video codec detected: $source_video_codec"

# Determine if we need to convert 10-bit video to 8-bit for H.264 compatibility
needs_10bit_to_8bit_conversion=false
if [[ $is_10bit == true && ($video_encoder == "h264" || -z "$video_encoder") ]]; then
	needs_10bit_to_8bit_conversion=true
	echo "Detected 10-bit input. Forcing 8-bit pixel format (nv12) for H.264 compatibility."
elif [[ ($video_encoder == "h264" || -z "$video_encoder") && "$source_video_codec" != "h264" && $is_10bit == false ]]; then
	echo "Converting from $source_video_codec to h264 (8-bit input, no bit depth conversion needed)."
fi

# Remove extension from the input file
input_file_without_extension=${input_file##*/}
input_file_without_extension=${input_file_without_extension%.*}

# Check if the output_folder variable exists and variable output_file not exists
if [[ -n $output_folder && -z $output_file ]]; then
	output_file="$output_folder/$input_file_without_extension"
fi

# If variable output_file is not set, use same folder as input file
: ${output_file:="$input_file_without_extension"}

# Change to case insensitive
shopt -s nocasematch

# Improved version that uses output_format when available
if [[ -n "$output_format" ]]; then
	# Remove any existing extension
	output_basename="${output_file%.*}"
	# Add the output format extension
	output_file="${output_basename}.${output_format}"
elif [[ ! "$output_file" =~ \.(mp4|avi|mkv|mov|wmv|flv|webm|m4v|mpeg|mpg)$ ]]; then
	# If no format is defined and no recognized extension is present, use mp4
	output_file="$output_file.mp4"
fi

# Back to case sensitive
shopt -u nocasematch

# Info about the input file
ffprobe_result=$(ffprobe -select_streams s -show_entries stream=index:stream_tags=language,title:stream_disposition=forced -of csv=p=0 "$input_file" 2>&1)

##############
# Subtitles
##############
# Get subtitle codec information to filter only SRT subtitles
subtitle_codec_info=$(ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of csv=p=0 "$input_file")

# Extract IDs of SRT (subrip) subtitles
subtitle_list_srt_ids=""
while IFS=, read -r index codec_name; do
	if [[ "$codec_name" == "subrip" ]]; then
		subtitle_list_srt_ids+="$index "
	fi
done <<<"$subtitle_codec_info"

# Filter simplified
subtitle_list_simplified=$(grep '^[0-9],' <<<"$ffprobe_result")

# Convert to an array
mapfile -t subtitles <<<"$subtitle_list_simplified"

# If not specified, use extract
: ${subtitle_extract:=extract}

# Subtitle handling
if [[ $subtitle_extract == "embedded" ]]; then
	output_extension="${output_file##*.}"
	subtitle_cmd="" # Initialize as empty

	if [[ "$output_extension" == "mp4" ]]; then
		# For MP4, find compatible SRT streams and convert them to mov_text
		map_cmd=""
		subtitle_info_for_embed=$(ffprobe -v error -select_streams s -show_entries stream=index,codec_name -of csv=p=0 "$input_file")
		while IFS=, read -r index codec_name; do
			if [[ "$codec_name" == "subrip" ]]; then
				map_cmd+=" -map 0:$index"
			fi
		done <<<"$subtitle_info_for_embed"
		# If we found any SRT streams, set the command
		if [[ -n "$map_cmd" ]]; then
			subtitle_cmd="$map_cmd -c:s mov_text"
		fi
	else
		# For other containers like MKV, copy all subtitle streams
		subtitle_cmd="-map 0:s? -c:s copy"
	fi
elif [[ $subtitle_extract == "extract" ]]; then
	if [[ -n $subtitle_list_srt_ids ]]; then
		# Declare an associative array to track language code usage
		declare -A language_code_usage

		# Loop through the array
		for i in "${subtitles[@]}"; do

			IFS=',' read -r index disposition language_code title <<<"$i"

			# Only process the subtitle if it is in the list of srt subtitles
			# Use word boundary matching to avoid false positives (e.g., index 4 matching in 14)
			if [[ " $subtitle_list_srt_ids " =~ " $index " ]]; then

				# Remove white spaces and special characters from the language code
				language_code=${language_code// /}
				# Remove quotes, parentheses and other problematic characters
				language_code=${language_code//\"/}
				language_code=${language_code//\'/}
				language_code=${language_code//\(/}
				language_code=${language_code//\)/}
				language_code=${language_code//\[/}
				language_code=${language_code//\]/}

				# Verify if the language code is valid
				if [[ -z $language_code || $language_code == "0" ]]; then
					language_code="und" # undetermined, if the language code is empty
				fi

				# Check if this is a forced subtitle
				if [[ $title == *"(Forced)"* || $disposition == "1" ]]; then
					suffix=".forced"
				else
					suffix=""
				fi

				# Only increment the counter for non-forced subtitles
				# Forced subtitles have a different suffix, so they don't need numbering
				if [[ -z $suffix ]]; then
					# Check if the language code has been used before
					if [[ -n "${language_code_usage[$language_code]}" ]]; then
						language_code_count=${language_code_usage[$language_code]}
						((language_code_count++))
						language_code_usage[$language_code]=$language_code_count
						language_code="${language_code}${language_code_count}"
					else
						language_code_usage[$language_code]=1
					fi
				fi

				output_file_srt="${output_file%.*}.$language_code$suffix.srt"

				subtitle_cmd+=" -map 0:$index -c copy \"$output_file_srt\" "

			fi
		done
	fi
elif [[ $subtitle_extract == "none" ]]; then
	subtitle_cmd=""
fi

##############
# Audio
##############
# If not specified, use copy.
: ${audio_handling:=copy}
original_audio_handling=$audio_handling

# If audio codec not specified, use aac
: ${audio_codec:=aac}

# Function to build the audio command string based on the current audio_handling value
function build_audio_cmd() {
	audio_cmd="" # Reset audio command
	if [[ $audio_handling == "copy" ]]; then
		audio_info=$(ffprobe -v error -select_streams a -show_entries stream=index:stream_tags=language -of csv=p=0 "$input_file")
		mapfile -t audio_streams <<<"$audio_info"
		echo "DEBUG: Found ${#audio_streams[@]} audio streams"
		for i in "${!audio_streams[@]}"; do
			IFS=',' read -r index_audio language_audio <<<"${audio_streams[$i]}"
			echo "DEBUG: Mapping audio stream $index_audio (language: $language_audio)"
			audio_cmd+="-map 0:$index_audio -c:a:$i copy "
		done
		echo "DEBUG: Final audio_cmd: $audio_cmd"
	elif [[ $audio_handling == "reencode" ]]; then
		audio_info=$(ffprobe -v error -select_streams a -show_entries stream=index:stream_tags=language -of csv=p=0 "$input_file")
		mapfile -t audio_streams <<<"$audio_info"
		unset i
		for i in "${!audio_streams[@]}"; do
			IFS=',' read -r index_audio language_audio <<<"${audio_streams[$i]}"

			# Get the number of channels for this specific audio track
			if [[ -n $audio_channels ]]; then
				channels=$audio_channels
			else
				channels=$(ffprobe -v error -select_streams a:$i -show_entries stream=channels -of csv=p=0 "$input_file")
			fi

			# Remove white spaces from the language_audio
			language_audio=${language_audio// /}

			# Verify if the language_audio is empty, if not set it to undetermined
			if [[ -z $language_audio ]]; then
				language_audio="und"
			fi

		if [[ -z $audio_bitrate ]]; then
			audio_bitrate_value=$((channels * 32))k
		else
			audio_bitrate_value=$audio_bitrate
		fi

		# Build audio encoding command based on codec
		case $audio_codec in
		opus)
			# Opus: excellent quality, especially at lower bitrates
			audio_cmd+="-map 0:$index_audio -c:a:$i libopus -b:a:$i $audio_bitrate_value -ac:a:$i $channels "
			;;
		ac3)
			# AC3 (Dolby Digital): for multichannel audio
			audio_cmd+="-map 0:$index_audio -c:a:$i ac3 -b:a:$i $audio_bitrate_value -ac:a:$i $channels "
			;;
		*)
			# AAC: default, best compatibility
			audio_cmd+="-map 0:$index_audio -c:a:$i aac -aac_coder fast -profile:a aac_low -b:a:$i $audio_bitrate_value -ac:a:$i $channels "
			;;
		esac

	done
	elif [[ $audio_handling == "none" ]]; then
		audio_cmd="-an"
	fi
}

##############
# Video
##############
# if video_quality is default, remove this part
video_quality=${video_quality/default/}

# If quality is not specified, use medium
: ${video_quality:=medium}

case $video_quality in
veryhigh)
	cq_value=19
	qp_value=18
	global_quality=18
	cq_value_nvidia=19
	;;
high)
	cq_value=24
	qp_value=21
	global_quality=21
	cq_value_nvidia=24
	;;
medium)
	cq_value=28
	qp_value=24
	global_quality=24
	cq_value_nvidia=28
	;;
low)
	cq_value=31
	qp_value=27
	global_quality=27
	cq_value_nvidia=31
	;;
verylow)
	cq_value=34
	qp_value=30
	global_quality=30
	cq_value_nvidia=34
	;;
superlow)
	cq_value=38
	qp_value=33
	global_quality=33
	cq_value_nvidia=38
	;;
esac

if [[ $preset == "default" ]]; then
	unset preset
fi

# If preset is not specified, use medium for GPU and faster for software encoding
if [[ -z $preset ]]; then
	language_audio="und"
	preset=medium
	software_preset=faster
else
	software_preset=$preset
fi

# Convert preset for NVIDIA
case $preset in
ultrafast) nvidia_preset=1 ;;
veryfast) nvidia_preset=2 ;;
faster) nvidia_preset=3 ;;
slow) nvidia_preset=5 ;;
veryslow) nvidia_preset=6 ;;
*) nvidia_preset=4 ;; # Default to medium
esac

#######################
# Create ffmpeg command
#######################
# Function to build the main encoder command strings
function build_encoder_commands() {
	general_params="-i \"$input_file\" -map 0:v:0 $audio_cmd"
	copy_video="$general_params -c:v copy"

	# Select appropriate encoder command based on the specified video encoder
	case $video_encoder in
	h265)
		encoder_nvenc="$general_params -c:v hevc_nvenc -rc:v constqp -qp $qp_value -b:v 0K -tune:v hq -preset:v p$nvidia_preset -temporal-aq 1 -rc-lookahead 100"
		encoder_vulkan="$general_params -c:v hevc_vulkan -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_vaapi="$general_params -c:v hevc_vaapi -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_qsv="$general_params -c:v hevc_qsv -preset $preset -look_ahead_depth 100 -low_delay_brc 1 -extbrc 1 -global_quality $global_quality -mbbrc 1 -adaptive_i 1 -adaptive_b 1"
		encoder_software="$general_params -c:v libx265 -preset $software_preset -look_ahead_depth 100 -crf $qp_value"
		;;
	av1)
		encoder_nvenc="$general_params -c:v av1_nvenc -rc:v constqp -qp $qp_value -b:v 0K -tune:v hq -preset:v p$nvidia_preset -temporal-aq 1 -rc-lookahead 100"
		encoder_vaapi="$general_params -c:v av1_vaapi -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_qsv="$general_params -c:v av1_qsv -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_software="$general_params -c:v libsvtav1 -crf $cq_value"
		;;
	vp9)
		encoder_nvenc="$general_params -c:v vp9_nvenc -rc:v constqp -qp $qp_value -b:v 0K -tune:v hq -preset:v p$nvidia_preset -temporal-aq 1 -rc-lookahead 100"
		encoder_vaapi="$general_params -c:v vp9_vaapi -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_qsv="$general_params -c:v vp9_qsv -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_software="$general_params -c:v libvpx-vp9 -preset $software_preset -look_ahead_depth 100 -crf $cq_value  -speed 1"
		;;
	*)
		# Default is h264
		encoder_nvenc="$general_params -c:v h264_nvenc -rc:v vbr -cq $cq_value_nvidia -tune:v hq -preset:v p$nvidia_preset -temporal-aq 1 -rc-lookahead 100"
		encoder_vulkan="$general_params -c:v h264_vulkan -preset $preset -look_ahead_depth 100 -global_quality $global_quality"
		encoder_vaapi="$general_params -c:v h264_vaapi -preset $preset -profile:v high -level:v 4.1 -rc_mode 1 -fps_mode cfr -g 60 -keyint_min 60 -look_ahead_depth 100 -low_delay_brc 1 -extbrc 1 -global_quality $global_quality"
		encoder_qsv="$general_params -c:v h264_qsv -preset $preset -profile:v high -level:v 4.1 -fps_mode cfr -g 48 -keyint_min 48 -look_ahead_depth 100 -low_delay_brc 1 -extbrc 1 -global_quality $global_quality -mbbrc 1 -adaptive_i 1 -adaptive_b 1"
		encoder_software="$general_params -c:v libx264 -preset $software_preset -profile:v high -level:v 4.1 -crf $qp_value -look_ahead_depth 100"
		;;
	esac
}

# If gpu = auto change gpu to empty
gpu=${gpu/auto/}

# Generic options for ffmpeg
ffmpeg_generic_options="-movflags +faststart -y \"$output_file\""

if [[ $only_extract_subtitles = 1 ]]; then
	general_params="-i \"$input_file\" $subtitle_cmd"
	echo "Running command: $ffmpeg_executable $general_params"
	eval $ffmpeg_executable "$general_params"
	exit $?
fi

# Function to attempt the conversion using the 3-stage fallback (GPU -> Partial -> CPU)
function attempt_conversion() {
	# This function now re-initializes all hardware-specific variables to ensure a clean state

	# If gpu empty auto detect
	if [[ -z $gpu ]]; then
		devices=$(lspci)
	else
		devices="VGA $gpu"
	fi

	init_qsv='-init_hw_device vaapi=va:,driver=iHD,kernel_driver=i915 -init_hw_device qsv=qs@va'
	init_vaapi='-init_hw_device vaapi'
	init_vulkan='-init_hw_device vulkan'
	init_nvidia='-init_hw_device cuda'
	decoder_qsv='-filter_hw_device qs -hwaccel qsv -hwaccel_output_format qsv'
	decoder_vaapi='-hwaccel vaapi -hwaccel_output_format vaapi'
	decoder_vulkan='-hwaccel vulkan -hwaccel_output_format vulkan'
	decoder_cuda='-hwaccel cuda -hwaccel_output_format cuda'

	local scale_filter_name="scale"

	# Handle explicit software encoding request
	if [[ "$gpu" == "software" ]]; then
		encoder_format=$encoder_software
		force_software=1
	elif [[ "$force_encoder" == "vulkan" || "$gpu" == "vulkan" ]]; then
		decoder_format="$decoder_vulkan"
		encoder_format="$encoder_vulkan"
		init_hardware="$init_vulkan"
		scale_filter_name="scale_vulkan"
	elif grep -qiE '(VGA|Display controller|3d).*nvidia' <<<"$devices"; then
		decoder_format="$decoder_cuda"
		encoder_format="$encoder_nvenc"
		init_hardware="$init_nvidia"
		scale_filter_name="scale_cuda"
	elif grep -qiE '(VGA|Display controller).*(\bAMD\b|\bATI\b)' <<<"$devices"; then
		decoder_format="$decoder_vaapi"
		encoder_format="$encoder_vaapi"
		init_hardware="$init_vaapi"
		scale_filter_name="scale_vaapi"
	elif grep -qiE '(VGA|Display controller).*intel' <<<"$devices"; then
		decoder_format="$decoder_qsv"
		encoder_format="$encoder_qsv"
		init_hardware="$init_qsv"
		scale_filter_name="scale_qsv"
	else
		encoder_format=$encoder_software
	fi

	if [[ $force_software = 1 ]]; then
		encoder_format=$encoder_software
	fi

	if [[ $force_copy_video = 1 ]]; then
		encoder_format=$copy_video
	fi
	if [[ -n $force_encoder ]]; then
		encoder_var="encoder_${force_encoder}"
		encoder_format=${!encoder_var}
		init_hardware="${!encoder_var/init/}"
	fi

	if [[ -n $force_decoder ]]; then
		decoder_var="decoder_${force_decoder}"
		decoder_format=${!decoder_var}
		init_hardware="${!decoder_var/init/}"
	fi

	#####################################################
	# --- Encode and Decode video in GPU filter chain ---
	#####################################################
	local video_filter_chain=""
	local filter_parts=()

	# # 1. Add 10-bit to 8-bit conversion filter if needed (only for GPU encode with hardware decode)
	# Note: For 8-bit video, this conversion is NOT needed
	if [[ $needs_10bit_to_8bit_conversion == true && -z "$video_filter" ]]; then
		force_8bit_color="${scale_filter_name}=format=nv12,"
	fi

	# if [[ -n "$video_filter" ]]; then
	# 	filter_parts+=("hwdownload,format=nv12")
	# fi

	# 2. Add user-provided filters (like scaling)
	if [[ -n "$video_filter" ]]; then
		local user_filters=$video_filter
		filter_parts+=("$user_filters")
	fi

	if [[ "$video_resolution" && ${#filter_parts[@]} -gt 0 ]]; then
		video_filter_chain="-vf ${force_8bit_color}hwdownload,format=nv12,"
		video_filter_chain+="$(
			IFS=,
			echo "${filter_parts[*]}"
		)"
		video_filter_chain+=",hwupload,$scale_filter_name=${video_resolution/x/:}"

	elif [[ ${#filter_parts[@]} -gt 0 ]]; then
		video_filter_chain="-vf ${force_8bit_color}hwdownload,format=nv12,"
		video_filter_chain+="$(
			IFS=,
			echo "${filter_parts[*]}"
		)"

	elif [[ -n "$video_resolution" ]]; then
		video_filter_chain="-vf ${force_8bit_color}$scale_filter_name=${video_resolution/x/:}"

	elif [[ $needs_10bit_to_8bit_conversion == true ]]; then
		video_filter_chain="-vf $force_8bit_color"

	fi

	# --- Execute Conversion Attempts ---

	# Extract subtitles first if needed (separate command to avoid output conflicts)
	if [[ -n "$subtitle_cmd" && "$subtitle_extract" == "extract" ]]; then
		echo "Extracting subtitles..."
		eval $ffmpeg_executable -i \"$input_file\" $subtitle_cmd 2>/dev/null
	fi

	if [[ "$force_copy_video" = "1" ]]; then
		echo "Generating file without re-encoding"
		echo "Running command: $ffmpeg_executable $options $decoder_format $encoder_format  $ffmpeg_generic_options"
		eval $ffmpeg_executable "$options" "$decoder_format" "$encoder_format" "$ffmpeg_generic_options"
		# After copy always exit
		exit $?
	fi
	
	# Try to encode with the GPU
	if [[ "$encoder_format" != "$encoder_software" && "$gpu_partial" != "1" ]]; then
		echo "Encode mode: Decode GPU, encode GPU"
		echo "Detected encode mode: Decode GPU, encode GPU (Aceleração total de GPU)"
		echo "Running command: $ffmpeg_executable $init_hardware $options $decoder_format $encoder_format $video_filter_chain $ffmpeg_generic_options"
		echo ""
		echo "FFmpeg command:"
		echo "$ffmpeg_executable $init_hardware $options $decoder_format $encoder_format $video_filter_chain $ffmpeg_generic_options"
		eval $ffmpeg_executable "$init_hardware" "$options" "$decoder_format" "$encoder_format" "$video_filter_chain" "$ffmpeg_generic_options"
		# Store the exit code while preserving $? for later checks
		exit_code=$?
		if [[ $exit_code != 0 ]]; then
			echo "Conversion failed!"
		fi
	else
		# GPU encoding skipped, set exit_code to trigger software encoding
		exit_code=1
	fi

	# If encoding not successful, and not exit with 255 (interrupted by user), and not software encoding, try with the second option
	if [[ ($exit_code != 0 && $exit_code != 255 && $force_software != 1 && $force_copy_video != 1) || "$gpu_partial" == "1" ]]; then
		echo "Encode mode: Decode Software, Encode GPU"
		echo "Detected encode mode: Decode Software, Encode GPU (Decodificação de Software e codificação por GPU)"

		#####################################################
		# --- Encode GPU Decode CPU filter chain ---
		#####################################################
		local partial_video_filter_chain=""
		local partial_filter_parts=()

		# For partial GPU, we need to add hwupload at the beginning of the filter chain.
		# This moves the software-decoded frame (e.g., nv12) to the GPU's memory.
		if [[ ${encoder_format,,} =~ nvenc ]]; then
			partial_filter_parts+=("format=nv12" "hwupload_cuda")
		elif [[ ${encoder_format,,} =~ vulkan ]]; then
			partial_filter_parts+=("format=nv12" "hwupload=derive_device=vulkan")
		elif [[ ${encoder_format,,} =~ vaapi ]]; then
			partial_filter_parts+=("format=nv12" "hwupload=derive_device=vaapi")
		elif [[ ${encoder_format,,} =~ qsv ]]; then
			partial_filter_parts+=("format=nv12" "hwupload=extra_hw_frames=64,format=qsv")
		fi

		# Add user-provided filters after the upload
		if [[ -n "$video_filter" ]]; then
			partial_filter_parts+=("$video_filter")
		fi

		# Add scaling filter if specified
		if [[ -n "$video_resolution" ]]; then
			# For partial mode, scaling happens on the GPU after upload
			partial_filter_parts+=("$scale_filter_name=${video_resolution/x/:}")
		fi

		# Build the final filter chain string if there are any filters
		if [[ ${#partial_filter_parts[@]} -gt 0 ]]; then
			partial_video_filter_chain="-vf $(IFS=,; echo "${partial_filter_parts[*]}")"
		fi

		echo "Running command: $ffmpeg_executable $init_hardware $options $encoder_format $partial_video_filter_chain $ffmpeg_generic_options"
		echo ""
		echo "FFmpeg command:"
		echo "$ffmpeg_executable $init_hardware $options $encoder_format $partial_video_filter_chain $ffmpeg_generic_options"
		eval $ffmpeg_executable "$init_hardware" "$options" "$encoder_format" "$partial_video_filter_chain" "$ffmpeg_generic_options"
		exit_code=$?
		if [[ $exit_code != 0 ]]; then
			echo "Conversion failed!"
		fi
	fi

	# If encoding not successful, and not exit with 255 (interrupted by user), try with software
	if [[ ($exit_code != 0 && $exit_code != 255 && $force_software != 1 && $force_copy_video != 1) || $force_software == 1 || ("$encoder_format" == "$encoder_software") ]]; then
		echo "Encode mode: Decode Software, Encode Software"
		echo "Detected encode mode: Decode Software, Encode Software (Codificação de software)"
		# Software mode uses the original video_filter variable without hw acceleration
		local software_filter_chain=""
		if [[ -n "$video_filter" ]]; then
			software_filter_chain="-vf $video_filter"
		fi
		if [[ -n "$video_resolution" ]]; then
			if [[ -z "$software_filter_chain" ]]; then
				software_filter_chain="-vf scale=${video_resolution/x/:}"
			else
				software_filter_chain+=",scale=${video_resolution/x/:}"
			fi
		fi

		echo "Running command: $ffmpeg_executable $options $encoder_software $software_filter_chain $ffmpeg_generic_options"
		echo ""
		echo "FFmpeg command:"
		echo "$ffmpeg_executable $options $encoder_software $software_filter_chain $ffmpeg_generic_options"
		eval $ffmpeg_executable "$options" "$encoder_software" "$software_filter_chain" "$ffmpeg_generic_options"
		exit_code=$?
	fi
}

# --- First Pass ---
# Build commands with initial user settings and attempt conversion.
build_audio_cmd
build_encoder_commands
attempt_conversion

# --- Audio Fallback Pass ---
# If the first pass failed and we were trying to copy audio, it's likely due to an
# incompatible audio codec for the container. Let's try again by re-encoding the audio.
if [[ $exit_code != 0 && $exit_code != 255 && "$original_audio_handling" == "copy" ]]; then
	echo -e "${COLOR_YELLOW}Audio stream copy may have failed. Retrying with audio re-encoding...${COLOR_RESET}"

	# Force re-encode and rebuild all commands
	audio_handling="reencode"
	build_audio_cmd
	build_encoder_commands

	# Attempt the conversion again with the new settings
	attempt_conversion
fi

# --- Final Exit Code Check ---
if [[ $exit_code != 0 ]]; then
	if [[ $exit_code == 255 ]]; then
		echo -e "${COLOR_YELLOW}Conversion interrupted by user.${COLOR_RESET}"
	else
		echo -e "${COLOR_YELLOW}Conversion failed with an unexpected error. Exit code: $exit_code.${COLOR_RESET}"
	fi
	exit $exit_code
fi
