### vim:ft=zsh:foldmethod=marker
### use your favorite editor to edit tags of audio files
### This supports the file-types and tags atag() supports.
###
### Frank Terbeck <ft@bewatermyfriend.org>
### Last-Modified: Mon Mar 10 10:45:50 2008
###
### URI: <http://ft.bewatermyfriend.org/comp/zsh.html>
###

emulate -L zsh
setopt extendedglob
setopt nullglob
setopt noksharrays

# variables {{{

local ATAGEDITOR tmpfile afile force dryrun line tmp usage
local -a atag_options tags
local -A tag_table

tag_table=(
    ARTIST          ar
    ALBUM           al
    TRACKNUMBER     tn
    TRACKTITLE      tt
    YEAR            y
    COMPILATION     comp
    GENRE           g
)

usage='atag-editor [-d|-f|-v] <FILE(s)>

  Load the tags for the specified audio files, list them
  in a file. Open that file in an editor. Re-Load the tags
  from that file and set them for the according files.

  Options:
    -d  run tag setting via atag() in dry run mode
    -f  hand the force option for tag setting (see atag()'\''s help)
    -v  run verbosely
    -h  this help text

'

ATAGEDITOR="${ATAGEDITOR:-"${VISUAL:-"${EDITOR}"}"}"

#}}}
# functions {{{

function ae_printf() {
    local format=$1 ; shift

    printf "${format}" "$@" >> "${tmpfile}"
}

function ae_addfile() {
    ae_printf '# %s\n' ${1}
}

function ae_data() {
    ae_printf '%s=%s\n' ${1} ${2}
}

function ae_writeheader() {
    ae_printf '### atag-editor\n###\n'
    ae_printf '### edit the tags to your needs\n'
    ae_printf '### empty lines and lines starting with three #-signs are ignored.\n'
    ae_printf '### lines starting with '\''# '\'' define the start of a new file.\n'
    ae_printf '### valid tags: ARTIST, ALBUM, TRACKNUMBER, TRACKTITLE,\n'
    ae_printf '###             YEAR, COMPILATION and GENRE.\n'
    ae_printf '### if you wish to do nothing, uncomment the next line.\n'
    ae_printf '#atag-editor-EXIT\n'
}

function cleanup() {
    rm "${tmpfile}"
}

function notags() {
    local old=$1 new=$2

    printf '%s: No tags defined. Skipping.\n' ${old}
    afile=${new}
}

TRAPINT TRAPHUP TRAPQUIT() {
    printf 'Caught fatal signal (%s).\n' "$1"
    printf 'Removing tempfile (%s).\n' "${tmpfile}"
    rm "${tmpfile}"
    printf 'Bailing out.\n'

    return $(( 128 + $1 ))
}

#}}}
# main() {{{

(( force   = 0 ))
(( dryrun  = 0 ))
(( verbose = 0 ))

while [[ $1 == -* ]] ; do
    case $1 in
        (-h)
            printf '%s' ${usage}
            return 0;;

        (-d)
            dryrun=1  ; shift;;
        (-f)
            force=1   ; shift;;
        (-v)
            verbose=1 ; shift;;
        (*)
            printf "unknown option %s\n\n" "$1"
            printf "${usage}"
            return 1;;
    esac
done

if [[ $# -eq 0 ]] ; then
    printf '%s' ${usage}
    return 0
fi

atag_options=()
(( dryrun > 0 )) && atag_options+=(-d)
(( force  > 0 )) && atag_options+=(-f)

(( no = 0 ))

printf 'Creating data file...\n'

tmpfile="$(mktemp -t atag-editor.XXXXXXXXXX)"
if [[ $? != 0 ]] ; then
    printf 'Could not create temporary file. Giving up.\n'
    return 1
fi

ae_writeheader

for afile in "$@" ; do
    printf '  . processing '\''%s'\''\n' ${afile}
    atag ${afile} -e
    if [[ $? != 0 ]] ; then
        printf '    + ERROR while processing file. Skipping.\n'
        continue
    fi

    (( no++ ))
    ae_printf '\n'
    ae_addfile ${afile}
    ae_data ARTIST ${_ar}
    ae_data ALBUM ${_al}
    ae_data TRACKNUMBER ${_tn}
    ae_data TRACKTITLE ${_tt}
    ae_data YEAR ${_yr}
    ae_data COMPILATION ${_comp}
    ae_data GENRE ${_gr}

done

printf '\n'

if (( no == 0 )) ; then
    printf 'All given files were invalid. Giving up early.\n'
    cleanup
    return 1
fi

printf 'Spawning editor (%s)...\n' ${ATAGEDITOR}
${ATAGEDITOR} ${tmpfile}

printf 'Processing new data file...\n'

afile=''
tags=()
while IFS='' read -r line ; do

    case ${line} in

        (('#'|)atag-editor-EXIT)
            if [[ ${line} != '#'* ]] ; then
                printf 'Exit command found. Stopping processing.\n'
                cleanup
                return 0
            fi

            continue
            ;;

        ("###"*|[ $'\t']#)
            continue
            ;;

        ((#b)"#" (*))
            tmp=${match[1]}

            if [[ -z ${afile} ]] ; then
                afile=${tmp}
                continue
            fi

            if (( ${#tags} == 0 )) ; then
                notags ${afile} ${tmp}
                continue
            fi

            (( verbose > 0 )) && \
                print -- atag ${(qqq)afile} ${atag_options} ${(@qqq)tags}

            atag ${afile} ${atag_options} ${tags}

            afile=${tmp}
            tags=()
            ;;

        ((#b)(ARTIST|ALBUM|TRACKNUMBER|TRACKTITLE|YEAR|COMPILATION|GENRE)=(*))
            tags+="${tag_table[${match[1]}]}=${match[2]}"
            ;;

        (*)
            printf 'Broken line (%s). Ignoring.\n' ${line}
            ;;

    esac

done < ${tmpfile}

# set the last file
if (( ${#tags} == 0 )) ; then
    notags ${afile} ""
fi

if [[ -n ${afile} ]] ; then
    (( verbose > 0 )) && \
        print -- atag ${(qqq)afile} ${atag_options} ${(@qqq)tags}

    atag ${afile} ${atag_options} ${tags}
fi

cleanup

# }}}