#! /bin/bash
#
# songanizer
# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
# Free Software Foundation, Inc.
# All rights reserved.
#
# This file is a part of GNU Songanizer.
#
# GNU Songanizer 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 2 of the License, or
# (at your option) any later version.
# 
# GNU Songanizer 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.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# -  Patrick Ohnewein <patrick.ohnewein@lugbz.org>
# -  Debarshi Ray     <rishi@gnu.org>


VERSION="1.0"
PROGRAM=`basename $0`
# use gettext for internationalization
. gettext.sh

TEXTDOMAIN=songanizer
export TEXTDOMAIN
TEXTDOMAINDIR=$(dirname $0)/../share/locale
export TEXTDOMAINDIR

# definition of the used error codes
EX_OK=0
EX_USAGE=64
EX_SOFTWARE=70
EX_CONFIG=78
if [ -f sysexits ]; then
    # If an external sysexits file exists, we source it. This allows the
    # external overwriting of the error codes.
    source sysexits
fi

# variables
find_options="-follow"
verbose=0
casesensitive=0
organize_artists=0
organize_comments=0
organize_genre=0
organize_initial=0
organize_years=0
organize_albums=0

# Prints a version message.
print_version ()
{
	eval_gettext "songanizer (GNU Songanizer) ${VERSION}"
	echo
	echo "Copyright (C) 2007 Free Software Foundation, Inc."
	echo "This is free software. You may redistribute copies of it under the terms of"
        echo "the GNU General Public License <http://www.gnu.org/licenses/gpl.html>."
	echo "There is NO WARRANTY, to the extent permitted by law."
	echo
	eval_gettext "Written by Patrick Ohnewein and Debarshi Ray."
	echo
}

# Prints a help message, explaining all the available options.
print_help ()
{
	gettext "Organizes files in a virtual directory structure."
	echo
	gettext "Options:"
	echo
	gettext " --all              organize on the basis of all the tags"
	echo
	gettext " --follow           follow symbolic links to directories"
	echo
	gettext " --no-follow        do not follow symbolic links to directories"
	echo
	gettext " -A|--artist        organize on the basis of the artist tag"
	echo
	gettext " -C|--comments      organize on the basis of the the comments tag"
	echo
	gettext " -G|--genre         organize on the basis of the genre tag"
	echo
	gettext " -I|--initial       organize on the basis of the initial letter"
	echo
	gettext " -Y|--year          organize on the basis of the year tag"
	echo
	gettext " -L|--album         organize on the basis of the album tag"
	echo
	gettext " -h|--help          print this help screen"
	echo
	gettext " -s|--casesensitive be case-sensitive when looking up the tags"
	echo
	gettext " -v|--verbose       activate verbose mode"
	echo
	gettext " --version          print version information"
	echo
	echo
	gettext "Report bugs to <bug-songanizer@gnu.org>."
	echo
}

# Prints a usage message, explaining how the script has to be called.
print_usage ()
{
	eval_gettext "Usage: ${PROGRAM} [options] BASE"
	echo
	print_help
}

TEMP=$(getopt -o :ACGIYLhsv --long all,follow,no-follow,artist,comments,genre,initial,year,album,help,casesensitive,verbose,version -n "$(basename $0)" -- "$@")

eval set -- "$TEMP"

while true ; do
	case "${1}" in
		--all) organize_artists=1
		       organize_comments=1
		       organize_genre=1
		       organize_initial=1
		       organize_years=1
		       organize_albums=1
		       shift;;
		-A|--artist) organize_artists=1; shift;;
		-C|--comments) organize_comments=1; shift;;
		-G|--genre) organize_genre=1; shift;;
		-I|--initial) organize_initial=1; shift;;
		-L|--album) organize_albums=1; shift;;
		-Y|--year) organize_years=1; shift;;
		--follow) find_options="-follow"; shift;;
		--no-follow) find_options=""; shift;;
		-s|--casesensitive) casesensitive=1; shift;;
		-v|--verbose) verbose=1; shift;;
		-h|--help) print_help ; exit ${EX_OK} ; shift;;
		--version) print_version ; exit ${EX_OK} ; shift;;
		--) shift ; break ;;
		*) gettext "Unimplemented option choosen. Use -h to visualize a help screen."; echo; exit "${EX_USAGE}" ;;
	esac
done

if [ $# -eq 0 ]; then
	print_usage
	exit "${EX_USAGE}"
fi
basedir="${1%/}"
tempdir="/tmp"
if [ "${basedir:0:1}" != "/" ]; then
	basedir="`pwd`/${basedir}"
fi

if [ ! -d "${basedir}" ]; then
	eval_gettext "Error ${PROGRAM}: Base directory ${basedir} doesn't exist!"
	echo
	exit "${EX_CONFIG}"
fi

IFS='
'

datadir="${basedir}/_data"
datadirs="${datadir}*"

for datadir_elem in ${datadirs}; do
	if [ ! -d "${datadir_elem}" ]; then
		eval_gettext "Error ${PROGRAM}: invalid or no data directory ${datadir_elem}"
		echo
		exit "${EX_CONFIG}"
	elif [ ${verbose} -ne 0 ]; then
		echo "Detected data directory: ${datadir_elem}"
	fi
done

# read in the data
if [ ${verbose} -ne 0 ]; then
	echo "find ${datadirs} ${find_options} \( -name "*.mp3" -or -name "*.ogg" \) -and -type f -print 2>/dev/null"
fi
datacontent="`find ${datadirs} ${find_options} \( -name "*.mp3" -or -name "*.ogg" \) -and -type f -print 2>/dev/null`"

# organize on the basis of the initial letter
if [ $organize_initial -ne 0 ]; then
	initialdir="${tempdir}/_initial"
	eval_gettext "Creating temporary directory ${initialdir}..."
	echo
	if [ -d "${initialdir}" ]; then
		eval_gettext "Removing left-over temporary directory ${initialdir}..."
		echo
		if [ "${verbose}" -ne 0 ]; then
			echo "rm -rf ${initialdir} 2>/dev/null"
		fi
		rm -rf "${initialdir}" 2>/dev/null
	fi
	if [ ${verbose} -ne 0 ]; then
		echo "mkdir ${initialdir} 2>/dev/null"
	fi
	mkdir "${initialdir}" 2>/dev/null
	if [ ! -d "${initialdir}" ]; then
		eval_gettext "Error ${PROGRAM}: Couldn't create directory ${initialdir}!"
		echo
		exit "${EX_SOFTWARE}"
	else
	for file in ${datacontent}; do
		filename=`basename ${file}`
		if [ "${casesensitive}" -ne 0 ]; then
			fileinitial="${filename:0:1}"
		else
			fileinitial="`echo ${filename:0:1} | tr [:upper:] [:lower:]`"
		fi
			destdir="${initialdir}/${fileinitial}"
			if [ ! -d "${destdir}" ]; then
				eval_gettext "Creating ${destdir} ...";
				echo
				if [ "${verbose}" -ne 0 ]; then echo "mkdir ${destdir} 2>/dev/null" ; fi
				mkdir "${destdir}" 2>/dev/null
			fi
			if [ ! -d "${destdir}" ]; then
				eval_gettext "Error ${PROGRAM}: Couldn't create directory ${destdir}!"
				echo
			else
				destfile="${destdir}/${filename}"
				if [ -e "${destfile}" ]; then
					eval_gettext "Warning ${PROGRAM}: Link already exists. File '${filename}' is probably contained in more than one data directory!"
					echo
				else
					eval_gettext "Linking ${file} to ${destfile} ..."
					echo
					if [ "${verbose}" -ne 0 ]; then
						echo "ln -s ${file} ${destfile} 2>/dev/null"
					fi
					ln -s "../..${file#${basedir}}" "${destfile}" 2>/dev/null
					if [ ! -L "${destfile}" ]; then
						eval_gettext "Error ${PROGRAM}: Couldn't link ${file} to ${destfile}!"
						echo
					fi
					
				fi
			fi
		done
	fi
	eval_gettext "Moving ${initialdir} to ${basedir} ..."
	echo
	if [ -d "${basedir}/_initial" ]; then
		echo "Removing old tag directory ${basedir}/_initial ..."
		if [ ${verbose} -ne 0 ]; then
			echo "rm -rf ${basedir}/_initial 2>/dev/null"
		fi
		rm -rf "${basedir}/_initial" 2>/dev/null
	fi
	if [ -d "${basedir}/_${tagname}" ]; then
		eval_gettext "Error ${PROGRAM}: Couldn't remove old tag
directory ${basedir}/_initial!"
		echo
		exit "${EX_SOFTWARE}"
	fi
	if [ "${verbose}" -ne 0 ]; then
		echo "mv ${initialdir} ${basedir} 2>/dev/null"
	fi
	mv "${initialdir}" "${basedir}" 2>/dev/null
	if [ ! -d "${basedir}/_initial" ]; then
		eval_gettext "Error ${PROGRAM}: Couldn't move ${initialdir} to ${basedir}!"
		echo
		if [ -d "${initialdir}" ]; then
			eval_gettext "Removing temporary directory ${initialdir}..."
			echo
			if [ "${verbose}" -ne 0 ]; then
				echo "rm -rf ${initialdir} 2>/dev/null"
			fi
			rm -rf "${initialdir}" 2>/dev/null
		fi
		exit "${EX_SOFTWARE}"
	fi
fi

#
# Function, which builds a directory structure consiting of symbolic
# links to the corresponding files in the _data directory.
# To determine the association the mp3info utility with the given
# tagpattern gets used.
#
# @param tagname name of ID3 tag on base of which to organize the links
#        i.e.: genre
# @param tagpattern pattern for mp3info
#        i.e.  %g\n
# @return Sets EXIT_CODE to EX_OK on success!
#
organizeOnBaseOfTag()
{
	local tagname="${1}"
	local tagpattern="${tagname} - "

	# pessimistic aproach
	EXIT_CODE="${EX_SOFTWARE}"

	if [ -n "${tagname}" ]; then

		tagdir="${tempdir}/_${tagname}"
		eval_gettext "Creating temporary directory ${tagdir}..."
		echo
		if [ -d "${tagdir}" ]; then
			eval_gettext "Removing left-over temporary directory ${tagdir}..."
			echo
			if [ "${verbose}" -ne 0 ]; then
				echo "rm -rf ${tagdir} 2>/dev/null"
			fi
			rm -rf "${tagdir}" 2>/dev/null
		fi
		if [ "${verbose}" -ne 0 ]; then
			echo "mkdir ${tagdir} 2>/dev/null"
		fi
		mkdir "${tagdir}" 2>/dev/null
		if [ ! -d "${tagdir}" ]; then
			eval_gettext "Error ${PROGRAM}: Couldn't create directory ${tagdir}!"
			echo
			EXIT_CODE="${EX_SOFTWARE}"
		else
			for file in ${datacontent}; do
				filename=`basename ${file}`
				if [ "${verbose}" -ne 0 ]; then
					echo "extract ${file} | grep \"${tagpattern}\" | head --lines=1 2>/dev/null"
				fi
				tag="`extract ${file} | grep \"${tagpattern}\" | head --lines=1 2>/dev/null`"
				tag="${tag#${tagpattern}}"
				if [ "${casesensitive}" -eq 0 ]; then
				        if [ "${verbose}" -ne 0 ]; then
					        echo "echo ${tag} | tr [:upper:] [:lower:]"
					fi
					tag="`echo ${tag} | tr [:upper:] [:lower:]`"
				fi
                    # exchange '/', which is invalid for directory
                    # names with the neutral character '-'
				tag=${tag//\//-}
                    # if the filetag begins with two dots '..' 
		    # than we exchange them with a '-', to avoid
		    # confusing the file operations
				tag=${tag//#../-}

				if [ -z "${tag}" ]; then
					tag="unknown"
				fi
					
				destdir="${tagdir}/${tag}"
				if [ ! -d "${destdir}" ]; then
					eval_gettext "Creating ${destdir} ..."
					echo
					if [ "${verbose}" -ne 0 ]; then
						echo "mkdir ${destdir} 2>/dev/null"
				 	fi
					mkdir "${destdir}" 2>/dev/null
				fi
				if [ ! -d "${destdir}" ]; then
					eval_gettext "Error ${PROGRAM}: Couldn't create directory ${destdir}!"
					echo
				else
					destfile="${destdir}/${filename}"
					if [ -e "${destfile}" ]; then
						eval_gettext "Warning ${PROGRAM}: Link already exists. File '${filename}' is probably contained in more than one data directory!"
						echo
					else
						eval_gettext "Linking ${file} to ${destfile} ..."
						echo
						if [ "${verbose}" -ne 0 ]; then
							echo "ln -s ${file} ${destfile} 2>/dev/null"
						fi
						ln -s "../..${file#${basedir}}" "${destfile}" 2>/dev/null
						if [ ! -L "${destfile}" ]; then
							eval_gettext "Error ${PROGRAM}: Couldn't link ${file} to ${destfile}!"
							echo
						fi
					fi
				fi
			done

	    # success
		EXIT_CODE="${EX_OK}"	

		fi
		eval_gettext "Moving ${tagdir} to ${basedir}..."
		echo
	# remove older tag directories
		if [ -d "${basedir}/_${tagname}" ]; then
			echo "Removing old tag directory ${basedir}/_${tagname}..."
			if [ "${verbose}" -ne 0 ]; then
				echo "rm -rf ${basedir}/_${tagname} 2>/dev/null"
			fi
			rm -rf "${basedir}/_${tagname}" 2>/dev/null
		fi
		if [ -d "${basedir}/_${tagname}" ]; then
			eval_gettext "Error ${PROGRAM}: Couldn't remove old tag directory ${basedir}/_${tagname}!"
			echo
			exit "${EX_SOFTWARE}"
		fi
		if [ "${verbose}" -ne 0 ]; then
			echo "mv ${tagdir} ${basedir} 2>/dev/null"
		fi
		mv "${tagdir}" "${basedir}" 2>/dev/null
		if [ ! -d "${basedir}/_${tagname}" ]; then
                	eval_gettext "Error ${PROGRAM}: Couldn't move ${tagdir} to ${basedir}!"
			echo
			if [ -d "${tagdir}" ]; then
				eval_gettext "Removing temporary directory ${tagdir}..."
				echo
				if [ "${verbose}" -ne 0 ]; then
					echo "rm -rf ${tagdir} 2>/dev/null"
				fi
				rm -rf "${tagdir}" 2>/dev/null
			fi
			exit "${EX_SOFTWARE}"
		fi
	else
		eval_gettext "Error ${PROGRAM}: missing parameter: tagname = ${tagname}."
		echo
		EXIT_CODE="${EX_SOFTWARE}"
	fi
}

if [ ${organize_genre} -ne 0 ]; then
    organizeOnBaseOfTag "genre"
fi

if [ ${organize_artists} -ne 0 ]; then
    organizeOnBaseOfTag "artist"
fi

if [ ${organize_albums} -ne 0 ]; then
    organizeOnBaseOfTag "album"
fi

if [ ${organize_years} -ne 0 ]; then
    organizeOnBaseOfTag "date"
fi

if [ ${organize_comments} -ne 0 ]; then
    organizeOnBaseOfTag "comments"
fi

eval_gettext "${PROGRAM}: Completed in ${SECONDS}sec."
echo
exit "${EX_OK}"

