#!/usr/bin/env bash

# Copyright © 2012-2026 ScriptFanix
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License v3 is includded in the LICENSE file
# at the root of the project.

updateTags() {
	local	reader			\

	# Build a WHERE clause fragment that excludes files already read by any
	# known reader version; files not matching any known version need a
	# re-read
	for reader in "${tagreaders[@]}"
	do
		tagreaderclause+="${tagreaderclause:+ AND }NOT tags.tagreader = \"$reader\""
	done
	# Query source files that need tag updates: either the file's
	# last_change differs from the stored tag row's last_change, or the
	# tagreader version has changed.
	# Only files linked to a destination with action=1 (transcode) are
	# included.
	echo '
		SELECT DISTINCT
			source_files.id,
			source_files.last_change,
			mime_type_actions.mime_text,
			source_files.filename,
			tags.album,
			tags.albumartist,
			tags.artist,
			tags.composer,
			tags.depth,
			tags.disc,
			tags.genre,
			tags.performer,
			tags.rating,
			tags.releasecountry,
			tags.replaygain_alb,
			tags.replaygain_trk,
			tags.title,
			tags.track,
			tags.year,
			tags.rate,
			tags.channels,
			tags.bitrate
		FROM			source_files
			INNER JOIN	destination_files
				ON	destination_files.source_file_id=source_files.id
			INNER JOIN	destinations
				ON	destination_files.destination_id=destinations.id
			INNER JOIN	mime_type_actions
				ON	destinations.id=mime_type_actions.destination_id
			INNER JOIN	tags
				ON	source_files.id=tags.source_file
		WHERE	mime_type_actions.id = source_files.mime_type
		AND	(
					CAST(tags.last_change AS TEXT)
					<>
					CAST(source_files.last_change AS TEXT)
				OR	('"$tagreaderclause"')
			)
		AND	mime_type_actions.action = 1
		ORDER BY source_files.id' >&3
	# Optionally cap the number of files processed in one run
(( maxbatch )) && echo "LIMIT $maxbatch" >&3
echo '
;

		SELECT "AtOM:NoMoreFiles";' >&3
	# Collect all result rows; sentinel "AtOM:NoMoreFiles" ends the loop
	read -u4 -r -d $'\0' line
	while ! [[ $line = AtOM:NoMoreFiles ]]
	do
		tagfiles+=("$line")
		(( filecount++ ))
		read -u4 -r -d $'\0' line
	done
	# Wrap all updates in a transaction	echo 'BEGIN TRANSACTION;' >&3
	for line in "${tagfiles[@]}"
	do
		# Split each row on the ::AtOM:SQL:Sep:: column separator
		sourcefileid=${line%%::AtOM:SQL:Sep::*}
		rest=${line#*::AtOM:SQL:Sep::}
		lastchange=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		mimetype=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		filename=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldalbum=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldalbumartist=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldartist=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldcomposer=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		olddepth=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		olddisc=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldgenre=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldperformer=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldrating=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldreleasecountry=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldreplaygain_alb=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldreplaygain_trk=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldtitle=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldtrack=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldyear=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldrate=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldchannels=${rest%%::AtOM:SQL:Sep::*}
		rest=${rest#*::AtOM:SQL:Sep::}
		oldbitrate=${rest%%::AtOM:SQL:Sep::*}
		((++count))
		# Show percentage progress in interactive mode
		(( cron )) || echo -en "\rTags: $((count*100/filecount))%"
		# Commit every 100 files: limit reprocessing on interrupt/crash
		if (( count % 100 == 0 ))
		then
			echo 'COMMIT;BEGIN TRANSACTION;' >&3
			(( debug ))					\
			&& echo -n "	$count files read, committing..."
		fi
		if getTags
		then
			# Set a dirty flag for each field that changed since
			# the last read;
			# only changed fields will be included in the UPDATE
			# statement below
			[[ $oldalbum	!= "$album"	]]&& ual=1
			[[ $oldalbumartist != "$albumartist" ]]&&uaa=1
			[[ $oldartist	!= "$artist"	]]&& uar=1
			[[ $oldcomposer	!= "$composer"	]]&& uco=1
			[[ $olddepth	!= "$depth"	]]&& ude=1
			[[ $olddisc	!= "$disc"	]]&& udi=1
			[[ $oldgenre	!= "$genre"	]]&& uge=1
			[[ $oldperformer != "$performer" ]]&& upe=1
			[[ $oldreleasecountry != "$releasecountry" ]]&& urc=1
			[[ $oldreplaygain_alb != "$replaygain_alb" ]]&& urpa=1
			[[ $oldreplaygain_trk != "$replaygain_trk" ]]&& urpt=1
			[[ $oldtitle	!= "$title"	]]&& uti=1
			[[ $oldtrack	!= "$tracknum"	]]&& utr=1
			[[ $oldyear	!= "$year"	]]&& uye=1
			[[ $oldrate	!= "$rate"	]]&& ura=1
			[[ $oldchannels	!= "$channels"	]]&& uch=1
			[[ $oldbitrate	!= "$bitrate"	]]&& ubi=1
			# Emit an Update with only dirty columns plus
			# last_change/tagreader.
			# ::AtOM:FT:: prefix forces text quoting even for
			# numeric-looking values.
			# Absent values become SQL NULL (no quotes).
			# Each ${flag:+col val} expands to nothing when the
			# flag is unset.
			Update tags					\
			${ual:+album		"${album:+::AtOM:FT::}${album:-NULL}"}\
			${uaa:+albumartist	"${albumartist:+::AtOM:FT::}${albumartist:-NULL}"}\
			${uar:+artist		"${artist:+::AtOM:FT::}${artist:-NULL}"}\
			${uco:+composer		"${composer:+::AtOM:FT::}${composer:-NULL}"}\
			${ude:+depth		"${depth:-NULL}"}	\
			${udi:+disc		"${disc:-NULL}"}	\
			${uge:+genre		"${genre:-NULL}"}	\
			${upe:+performer	"${performer:+::AtOM:FT::}${performer:-NULL}"}\
			${urr:+rating		"${rating:+::AtOM:FT::}${rating:-NULL}"}\
			${urc:+releasecountry	"${releasecountry:+::AtOM:FT::}${releasecountry:-NULL}"}\
			${urc:+releasecountry	"${releasecountry:+::AtOM:FT::}${releasecountry:-NULL}"}\
			${urpa:+replaygain_alb	"${replaygain_alb:-NULL}"}\
			${urpt:+replaygain_trk	"${replaygain_trk:-NULL}"}\
			${uti:+title		"${title:+::AtOM:FT::}${title:-NULL}"}\
			${utr:+track		"${track:+::AtOM:FT::}${tracknum:-NULL}"}\
			${uye:+year		"${year:-NULL}"}	\
			last_change		"$lastchange"		\
			${ura:+rate		"${rate:-NULL}"}	\
			${uch:+channels		"${channels:-NULL}"}	\
			${ubi:+bitrate		"${bitrate:-NULL}"}	\
			tagreader		"$tagreader"		\
			>/dev/null <<<"source_file = $sourcefileid"
			# Clear all tag variables and dirty flags for the next
			# iteration
			unset	genre			\
				albumartist		\
				year			\
				album			\
				disc			\
				artist			\
				tracknum		\
				title			\
				composer		\
				performer		\
				rating			\
				releasecountry		\
				replaygain_alb		\
				replaygain_trk		\
				rate			\
				depth			\
				bitrate			\
				channels		\
				oldgenre		\
				oldalbumartist		\
				oldyear			\
				oldalbum		\
				olddisc			\
				oldartist		\
				oldtracknum		\
				oldtitle		\
				oldcomposer		\
				oldperformer		\
				oldrating		\
				oldreleasecountry	\
				oldreplaygain_alb	\
				oldreplaygain_trk	\
				oldrate			\
				olddepth		\
				oldbitrate		\
				oldchannels		\
				ual			\
				uaa			\
				uar			\
				uco			\
				ude			\
				udi			\
				uge			\
				upe			\
				urr			\
				urc			\
				urpa			\
				urpt			\
				uti			\
				utr			\
				uye			\
				ura			\
				uch			\
				ubi
		fi
	done
	# Commit the final partial batch
	echo 'COMMIT;' >&3
	(( cron )) || echo -n $'\r'
	(( count )) && echo -n "Read tags from $count files."
	(( cron )) || echo -n $'\033[K'
	(( count )) && echo
	unset count tagfiles
}
