#!/bin/sh

# Launcher script to work around limitations in vgmplay:
#   OSS only - detects oss wrapper and allows explicit selection
#   Config must be in working dir - automatically cd/mktemps as needed
#   Can't open release packs - plays from temporary directories
#
# Original script by ZekeSulastin
# 7z support by Valley Bell

trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"' EXIT
trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"' HUP INT QUIT TERM SEGV

_set_output=
_output=
_pulse_server=
_log_path=
_binary=
_config=
_pwd="$(pwd)"
_host="$(hostname)"
_ext=
_unpack=false
_tmpdir=
_music_file=
_m3u_stub=
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}"
_padsp="$(command -v padsp 2>/dev/null)"
_aoss="$(command -v aoss 2>/dev/null)"

_help () {
cat <<EOF
Usage: vgm-player [options] filename
Uses vgmplay to play vgm, vgz, cmf, dro, m3u, and zip'd vgm packs.

Options:
        --help              display this help and exit
        --version           display vgmplay and vgm format versions and exit

 -a,    --alsa              force use of aoss (ALSA's OSS wrapper)
 -o,    --oss               force use of OSS (native OSS, OSSProxy)
 -p,    --pulse             force use of padsp (PulseAudio's OSS wrapper)
 -s <server>,
        --pulse-server=<server>
                            set alternate PulseAudio server (implies --padsp)
 -l,    --log               outputs to wav files in current directory
        --log-path=<path>   outputs to wav files in <path> (implies --log)

 WARNING: If you plan to log using this script, use --log or --log-path!
  Do NOT set logging on in a custom config, else the script logic will
  remove your logs!

        --binary=<binary>   use alternate vgmplay binary
        --config=<config>   use alternate configuration file

Keys (In-Player):
 Left/Right                 seek 5 seconds backward/forward
 Ctrl + Left/Right          seek 60 seconds backward/forward
 Space                      pause
 Escape (twice) or Q        quit program
 F                          fade out current track
 R                          restart current track
 PageUp or B                previous track in playlist
 PageDown or N              next track in playlist
EOF
exit 0
}

_to_stderr () {
    echo -e "$0: $2" 1>&2
    exit $1
}

_die () {
    case $1 in
        too_many_outputs )
            _to_stderr 64 "can only force one output type" ;;
        no_pulse_server )
            _to_stderr 64 "--pulse-server requires server parameter" ;;
        no_log_path )
            _to_stderr 64 "--log-to requires path parameter" ;;
        unknown_option )
            _to_stderr 64 "unrecognized option '$2'\nTry 'vgm-player --help' for more information." ;;
        no_aoss )
            _to_stderr 69 "alsa output forced but aoss not found" ;;
        no_oss )
            _to_stderr 74 "oss output forced but /dev/dsp unwritable or not present" ;;
        no_pulse )
            _to_stderr 69 "pulse output forced but padsp not found" ;;
        too_many_files )
            _to_stderr 64 "only one music file should be specified" ;;
        file_unreadable )
            _to_stderr 66 "unable to read specified input - does it exist?" ;;
        invalid_format )
            _to_stderr 66 "format unsupported" ;;
        invalid_binary )
            _to_stderr 69 "invalid binary specified" ;;
        no_binary )
            _to_stderr 64 "--binary requires binary parameter" ;;
        no_config )
            _to_stderr 64 "--config requires config parameter" ;;
        noread_config )
            _to_stderr 69 "specified config unreadable - does it exist?" ;;
        invalid_config )
            _to_stderr 64 "invalid config specified" ;;
        vgmplay_not_found )
            _to_stderr 69 "vgmplay binary not found" ;;
        no_unpacker )
            _to_stderr 69 "no suitable unpacker found" ;;
        tmpdir_failed )
            _to_stderr 70 "unable to create/use temporary working directory" ;;
        no_output )
            _to_stderr 69 "no usable otuput detected" ;;
        log_path_nowrite )
            _to_stderr 73 "log path unwriteable" ;;
        no_music_file )
            _to_stderr 64 "No music file specified." ;;
        * )
            _to_stderr 64 "Unhandled script error: $1" ;;
    esac
}

_set_output () {
    if [ -n "$_set_output" ] && [ "$1" != "$_set_output" ]; then
        _die too_many_outputs
    fi
    _set_output=$1

    case "$_set_output" in
        alsa )
            [ -x "$_aoss" ] || _die no_aoss ;;
        oss )
            [ -w /dev/dsp ] || _die no_oss ;;
        pulse )
            [ -n "$_padsp" ] || _die no_pulse ;;
        * )
            ;;
    esac
}

# Set terminal title for xterm/rxvt
case $TERM in
    xterm* | rxvt* )
        printf '%b' '\033]2;VGM Player\007' ;;
    * )
        ;;
esac

# Parse command line options
#   All single-letter options are exclusive
while :; do
    case $1 in
        --help )
            _help ;;
        -v | --version )
            echo "VGMPlay 0.40.9 player script"
            exit 0 ;;
        -a | --alsa )
            _set_output alsa
            shift ;;
        -o | --oss )
            _set_output oss
            shift ;;
        -p | --pulse )
            _set_output pulse
            shift ;;
        --pulse-server=* )
            _set_output pulse
            _pulse_server="${1#*=}"
            [ -n "$_pulse_server" ] || _die no_pulse_server
            shift ;;
        -s* )
            _set_output pulse
            _pulse_server="${1#-s}"
            if [ -z "$_pulse_server" ]; then
                _pulse_server="$2"
                shift
            fi
            [ -n "$_pulse_server" ] || _die no_pulse_server
            shift ;;
        -l | --log )
            _set_output log
            _log_path="$_pwd"
            shift ;;
        --log-path=* )
            _set_output log
            _log_path="${1#*=}"
            [ -d "$_log_path" ] || _die no_log_path
            shift ;;
        --config=* )
            _config="${1#*=}"
            [ -n "$_config" ] || _die no_binary
            [ -r "$_config" ] || _die noread_config
            file "$_config" | grep -q "ASCII" || _die invalid_config
            shift ;;
        --binary=* )
            _binary="${1#*=}"
            [ -n "$_binary"] || _die no_binary
            if [ ! -x "$_binary" ] || [ ! "${binary##*/}" = "vgmplay" ]; then
                die _invalid_binary
            fi
            shift ;;
        -* )
            _die unknown_option "$1" ;;
        -- )
            shift ;;
        "" )
            _die no_music_file ;;
        * )
            [ -z "$2" ] || _die too_many_files
            _ext="$(echo ${1##*.} | tr [:upper:] [:lower:])"
            case $_ext in
                vgm | vgz | cmf | dro | m3u | zip | 7z | vgm7z )
                    [ -r "$1" ] || _die file_unreadable
                    _music_file="$1";;
                * )
                    _die invalid_format
            esac
            case $_music_file in
                ~* | /* )
                    break ;;
                * )
                    _music_file="$_pwd/$_music_file" ;;
            esac
            break ;;
    esac
done

# Does `vgmplay` exist if --binary not specified?
#   Prefers vgmplay in working dir over any others
if [ -n "$_binary" ]; then
    break
elif [ -x "$_pwd/vgmplay" ] && [ -f "$_pwd/vgmplay" ]; then
    _binary="$_pwd/vgmplay"
else
    _binary="$(command -v vgmplay 2>/dev/null)"
fi
[ -n "$_binary" ] || _die vgmplay_not_found

# If a compressed pack, does a suitable unpacker exist?
case $_ext in
    zip )
        _unpack=true
        command -v unzip 2>/dev/null || _die no_unpacker ;;
    7z | vgm7z )
        _unpack=true
        command -v 7z 2>/dev/null || _die no_unpacker ;;
    vgm | vgz | cmf | dro | m3u )
        break ;;
    * )
        _die invalid_format ;;
esac

# Select config
if [ -r "$_config" ]; then
    break
else
    for i in "$_pwd" "$XDG_CONFIG_HOME/vgmplay" "$HOME/.config/vgmplay" "$XDG_CONFIG_DIRS/vgmplay" "$HOME/.local/share/vgmplay" "/etc/xdg/vgmplay" "/usr/local/share/vgmplay" "/usr/share/vgmplay"; do
        _config="$i/vgmplay.ini"
        [ -r "$_config" ] && break
        _config="$i/VGMPlay.ini"
        [ -r "$_config" ] && break
    done
fi
# vgmplay will function without a config, but will warn the user thus from inside the program

# Make temporary working directory for vgmplay
# Logdir availability checked here ...
#   Uses $XDG_RUNTIME_DIR, $TMPDIR, /tmp
#   Uses mktemp program for my sanity's sake
#   Assumes user isn't going to try to log into a directory w/ a preexisting vgmplay.ini
#       Seriously, if you're going to do that why would you use this helper to start with
if [ -d "$_log_path" ]; then
    command sed "s/^LogSound.*/LogSound = 1/" < "$_config" > "$_log_path/vgmplay.ini" || _die log_path_nowrite
elif [ -n "$XDG_RUNTIME_DIR" ]; then
    _tmpdir="$(command mktemp -d --tmpdir="$XDG_RUNTIME_DIR")" || _die tmpdir_fail
    command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail
else #mktemp defaults to tmpdir then /tmp!
    _tmpdir="$(command mktemp -d)" || _die tmpdir_fail
    command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail
fi

# Determine output to use if not forced
# Also sets pulse-server if detected
# PULSE_SERVER, oss, padsp, aoss, log set, error
if [ -z "$_set_output" ]; then
    if [ -n "$PULSE_SERVER" ] && [ -x "$_padsp" ]; then
        _set_output="pulse-server"
    elif [ -w /dev/dsp ]; then
        _set_output="oss"
    elif [ -x "$_padsp" ] && command pulseaudio --check ; then
        _set_output="pulse"
    elif [ -x "$_aoss" ]; then
        _set_output="alsa"
    elif [ -n "$_log_path" ]; then
        _set_output="log"
    else
        _die no_output
    fi
fi
[ -n "$_pulse_server" ] && _set_output=pulse-server

if [ "$_unpack" = true ]; then
    if [ "$_set_output" = log ]; then
        [ -d "$_log_path" ] && cd "$_log_path" || _die log_path_nowrite
    else
        [ -d "$_tmpdir" ] && cd "$_tmpdir" || _die tmpdir_failed
    fi
    if [ $_ext = "zip" ]; then
        command unzip "$_music_file" > /dev/null
    elif [ $_ext = "7z" ] || [ $_ext = "vgm7z" ]; then
        command 7z x "$_music_file" > /dev/null
    fi
    # Notes:
    #   - sed is used to remove the leading ./
    #   - find is used to scan subfolders as well
    _m3u_stub=$(command find . -iname '*.m3u' -print 2> /dev/null | head -n 1 | sed -e 's/^.\///')
    if [ -z "$_m3u_stub" ]; then
        _m3u_stub="List.m3u"
        command find . -iname '*.vgm' -o -iname '*.vgz' -print | sed -e 's/^.\///' > "$_m3u_stub" 2> /dev/null
    fi
    _music_file="$PWD/$_m3u_stub"
fi

# Plays back song with specified everything
case $_set_output in
    log )
        (   cd "$_log_path"
            "$_binary" "$_music_file"
        )
        command mv "${_music_file%/*}/"*.wav "${_log_path}"
        [ -n "$_music_stub" ] && rm -rf "$_log_path/$_music_stub" ;;
    pulse-server )
        _pulse_server="${_pulse_server:-$PULSE_SERVER}"
        (   cd "$_tmpdir"
            exec "$_padsp" -s "$_pulse_server" -n "VGM Player" -m "OSS Emulation (from $_host)" "$_binary" "$_music_file"
        ) ;;
    pulse )
        (   cd "$_tmpdir"
            exec "$_padsp" -n "VGM Player" -m "OSS Emulation" "$_binary" "$_music_file"
        ) ;;
    alsa )
        (   cd "$_tmpdir"
            exec "$_aoss" "$_binary" "$_music_file"
        ) ;;
    oss )
        (   cd "$_tmpdir"
            exec "$_binary" "$_music_file"
        ) ;;
esac
#read waitvar
# vim:ts=4:sw=4:et
