#!/bin/sh

[ -z $GTKDIALOG ] && GTKDIALOG=gtkdialog

# PlayMusic (c) Thunor 2011
# GNU GENERAL PUBLIC LICENSE Version 2, June 1991
# See http://www.gnu.org/licenses/gpl-2.0.txt

## Strip comments from the entire project using:
## for f in func* main; do sed -i -e '/## /d' -e '/ ##/d' $f; done

## ----------------------------------------------------------------------------
## Constants
## ----------------------------------------------------------------------------
## These are application defaults which may be updated from the rcfile.

TARGET=playmusic
TITLE=PlayMusic
VERSION=0.1.8
LOCAL_DATA_DIR=$HOME/.$TARGET
PACKAGE_DATA_DIR=`pwd`
DEBUG_CONTENT=0
DEBUG_TRANSITS=0
COVER_SEARCH_PATTERN="*.[JjPpBbGg][PpNnMmIi][GgPpFf]"
DIALOG_README_SHOW=1
## These don't require exporting.
AUTONEXT_INTERVAL=2000
COVER_SIZE=-1
COVER_THUMB_SIZE=96
WINDOW_WIDTH=224

## ----------------------------------------------------------------------------
## Includes
## ----------------------------------------------------------------------------
## These files are sourced from the PATH.

PATH=$PACKAGE_DATA_DIR:$PATH

. funcCoverSet
. funcDialogReadMeOpen
. funcgtkrcCreate
. funcPlaylistCreate
. funcrcfileRead
. funcrcfileWrite
. funcStop
. functmpGet
. functmpSet
. funcTrackInfoSet

## ----------------------------------------------------------------------------
## Local Functions
## ----------------------------------------------------------------------------

## Enable the embedding of comments within the XML.
Comment() { :; }

## Create the XML for the Previous/Next and Play/Pause buttons.
## On entry: $1 = button id
##           $2 = button id
funcButtonsCreate() {
	local index=-1
	local f

	for f in $@; do
		echo '
			<button>
				<input file stock="gtk-media-'`echo $f | tr A-Z a-z`'"></input>
				'`if [ $DEBUG_TRANSITS -ne 0 ]; then
					echo "<action>echo \"btn$f: IN\"</action>"
				fi`'
				<action>disable:tmrAutoNext</action>'
		if [ $f = Previous -o $f = Next ]; then
			echo '
				<action>. funcPreviousNext; funcPreviousNext '$index'</action>
				<action>refresh:muiCoverRefreshCheck</action>
				<action>refresh:muiTrackInfoRefreshCheck</action>'
		else
			echo '
				<action>. func'$f'; func'$f'</action>'
		fi
		echo '
				<action>refresh:nbkPlayPause</action>
				<action>refresh:muiAutoNextCheck</action>
				'`if [ $DEBUG_TRANSITS -ne 0 ]; then
					echo "<action>echo \"btn$f: OUT\"</action>"
				fi`'
			</button>'
		index=1
	done
}

## ----------------------------------------------------------------------------
## Main
## ----------------------------------------------------------------------------

## Create the local data directory if it doesn't already exist.
if [ ! -d $LOCAL_DATA_DIR ]; then
	mkdir $LOCAL_DATA_DIR
	if [ $? -ne 0 ]; then
		echo "Couldn't create $LOCAL_DATA_DIR"
		exit 1
	fi
fi

## Create the rcfile if it doesn't already exist.
if [ ! -f $LOCAL_DATA_DIR/${TARGET}rc ]; then
	funcrcfileWrite new
fi

## Process the command-line argument(s).
if [ "$1" = -h -o "$1" = --help ]; then
	echo "$TITLE $VERSION (C) 2011 Thunor
Usage: $TARGET [FILE or FOLDER]

A FILE creates a playlist of the folder non-recursively and plays the file.
A FOLDER creates a playlist of the folder recursively and plays the first file.
No arguments resumes playing the last playlist and the last file played.

See $PACKAGE_DATA_DIR/README for more information.
"
	exit 1
fi

## Create a temporary directory.
TEMP_DIR=`mktemp -d -t ${TARGET}.XXXXXXXX`
if [ $? -ne 0 ]; then
	echo "Couldn't create temporary directory."
	exit 1
fi

## Load the rcfile.
funcrcfileRead

## Create a custom style.
funcgtkrcCreate

## Create the initial files required.
functmpSet trackinforefreshcheck false
functmpSet coverrefreshcheck false
functmpSet dialogcovercheck false
functmpSet coverpresscheck false
functmpSet folderopencheck false
functmpSet autonextcheck false
functmpSet folderopen "[empty]"
functmpSet playerstate stopped
functmpSet trackinfotype 0
functmpSet playpause 0
functmpSet playerpid
funcPlaylistCreate "$1"
funcCoverSet
funcTrackInfoSet

## Create the XML for the cover dialog.
DIALOG_COVER='
<window name="winDialogCover" title="'$TITLE' - Cover" icon-name="'$TARGET'"
	window-position="2" border-width="0" resizable="false">
	<button name="btnDialogCover" relief="2">
		<variable>btnDialogCover</variable>
		<width>'$COVER_SIZE'</width>
		<height>'$COVER_SIZE'</height>
		<input file>'$TEMP_DIR'/cover</input>
		<action>closewindow:DIALOG_COVER</action>
	</button>
	<variable>DIALOG_COVER</variable>
</window>
'

## Create the XML for the main dialog.
echo '
<window name="winMain" title="'$TITLE'" icon-name="'$TARGET'" window-position="2"
	width-request="'$WINDOW_WIDTH'" resizable="false">
	<vbox>
		<entry name="entTrackInfo" editable="false" has-frame="false" can-focus="false"
			tooltip-text="Left-click to toggle between artist/album and track info">
			<variable>entTrackInfo</variable>
			<input file>'$TEMP_DIR'/trackinfo</input>
			<action signal="button-press-event">"
				if [ $BUTTON -eq 1 ]; then
					. funcTrackInfoSet; funcTrackInfoSet 2
				fi
			"</action>
			<action signal="button-press-event">refresh:entTrackInfo</action>
		</entry>
		<hbox>
'`Comment ##
## Because space-expand and space-fill have been set to true on the command-
## line and therefore will be the default for all widgets, we need to disable
## them for this button widget to keep it minimised and contained.
## `'
			<button name="btnCover" space-expand="false" space-fill="false"
				width-request="'$(($COVER_THUMB_SIZE + 10))'"
				height-request="'$(($COVER_THUMB_SIZE + 10))'"
				tooltip-text="Left-click: Next album
Middle-click: View cover
Right-click: Previous album">
				<variable>btnCover</variable>
				<width>'$COVER_THUMB_SIZE'</width>
				<height>'$COVER_THUMB_SIZE'</height>
				<input file>'$TEMP_DIR'/cover</input>
				'`if [ $DEBUG_TRANSITS -ne 0 ]; then
					echo "<action>echo \"btnCover-click: IN\"</action>"
					echo "<action signal=\"button-press-event\">echo \
						\"btnCover-button-press-event: IN\"</action>"
				fi`'
'`Comment ##
## Code to handle the "click" signal.
## `'
				<action>disable:tmrAutoNext</action>
				<action>"
					. funcFolderPreviousNext; funcFolderPreviousNext 1
					. funcWidgetSchedule
					funcWidgetSchedule muiCoverPressCheck coverpresscheck
				"</action>
				<action>refresh:muiCoverPressCheck</action>
'`Comment ##
## Code to handle the "button-press-event" signal. Clicking will also emit
## the same signal but it gets filtered out when checking the BUTTON.
## `'
				<action signal="button-press-event">disable:tmrAutoNext</action>
				<action signal="button-press-event">"
					if [ $BUTTON -eq 2 ]; then
						. functmpGet
						. funcWidgetSchedule
						funcWidgetSchedule muiDialogCoverCheck dialogcovercheck
						## tmrAutoNext was disabled on entry to these actions
						## to stop it interferring with folder navigation and
						## so we need to restore its correct state.
						if [ `functmpGet playerstate` = playing ]; then
							funcWidgetSchedule muiAutoNextCheck autonextcheck
						fi
						funcWidgetSchedule muiCoverPressCheck coverpresscheck
					elif [ $BUTTON -eq 3 ]; then
						. funcFolderPreviousNext; funcFolderPreviousNext -1
						. funcWidgetSchedule
						funcWidgetSchedule muiCoverPressCheck coverpresscheck
					fi
				"</action>
				<action signal="button-press-event">refresh:muiCoverPressCheck</action>
				'`if [ $DEBUG_TRANSITS -ne 0 ]; then
					echo "<action>echo \"btnCover-click: OUT\"</action>"
					echo "<action signal=\"button-press-event\">echo \
						\"btnCover-button-press-event: OUT\"</action>"
				fi`'
			</button>
			<vbox>
				<hbox>
					'"`funcButtonsCreate Previous Next`"'
				</hbox>
				<hbox>
					<button>
						<input file stock="gtk-media-stop"></input>
						'`if [ $DEBUG_TRANSITS -ne 0 ]; then
							echo "<action>echo \"btnStop: IN\"</action>"
						fi`'
						<action>disable:tmrAutoNext</action>
						<action>. funcStop; funcStop</action>
						<action>refresh:nbkPlayPause</action>
						'`if [ $DEBUG_TRANSITS -ne 0 ]; then
							echo "<action>echo \"btnStop: OUT\"</action>"
						fi`'
					</button>
'`Comment ##
## As changing stock icons at runtime is not possible, a notebook widget is
## used instead with one button on each page so we can change the buttons.
## `'
					<notebook show-tabs="false" show-border="false">
						'"`funcButtonsCreate Play Pause`"'
						<variable>nbkPlayPause</variable>
						<input file>'$TEMP_DIR'/playpause</input>
					</notebook>
				</hbox>
				<hbox>
					<button>
						<input file stock="gtk-open"></input>
						'`if [ $DEBUG_TRANSITS -ne 0 ]; then
							echo "<action>echo \"btnFolderOpen: IN\"</action>"
						fi`'
						<action>fileselect:entFolderOpen</action>
						'`if [ $DEBUG_TRANSITS -ne 0 ]; then
							echo "<action>echo \"btnFolderOpen: OUT\"</action>"
						fi`'
					</button>
					<button>
						<input file stock="gtk-quit"></input>
					</button>
				</hbox>
			</vbox>
		</hbox>
	</vbox>
	<vbox visible="false">
		<timer milliseconds="true" interval="'$AUTONEXT_INTERVAL'">
			<variable>tmrAutoNext</variable>
			<sensitive>false</sensitive>
			<action>. funcAutoNext; funcAutoNext</action>
			<action>refresh:muiCoverRefreshCheck</action>
			<action>refresh:muiTrackInfoRefreshCheck</action>
		</timer>
		<menubar>
			<menu>
'`Comment ##
## There are several checkbox menuitems here that are being used to execute
## action functions conditionally and they are all named *Check because they
## are checking a value before effecting the target widget. They sit in-between
## the source widget and the target widget and instead of refreshing the target
## widget you would call funcWidgetSchedule and then refresh the check widget.
## `'
				<menuitem checkbox="false">
					<variable>muiAutoNextCheck</variable>
					<input file>'$TEMP_DIR'/autonextcheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiAutoNextCheck: IN\"</action>"
					fi`'
					<action>enable:tmrAutoNext</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiAutoNextCheck: OUT\"</action>"
					fi`'
				</menuitem>
				<menuitem checkbox="false">
					<variable>muiCoverRefreshCheck</variable>
					<input file>'$TEMP_DIR'/coverrefreshcheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiCoverRefreshCheck: IN\"</action>"
					fi`'
					<action>. funcCoverSet; funcCoverSet</action>
					<action>refresh:btnCover</action>
					<action>refresh:btnDialogCover</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiCoverRefreshCheck: OUT\"</action>"
					fi`'
				</menuitem>
				<menuitem checkbox="false">
					<variable>muiTrackInfoRefreshCheck</variable>
					<input file>'$TEMP_DIR'/trackinforefreshcheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiTrackInfoRefreshCheck: IN\"</action>"
					fi`'
					<action>. funcTrackInfoSet; funcTrackInfoSet</action>
					<action>refresh:entTrackInfo</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiTrackInfoRefreshCheck: OUT\"</action>"
					fi`'
				</menuitem>
				<menuitem checkbox="false">
					<variable>muiDialogCoverCheck</variable>
					<input file>'$TEMP_DIR'/dialogcovercheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiDialogCoverCheck: IN\"</action>"
					fi`'
'`Comment ##
## Something to be aware of: launching will terminate the processing of further
## actions both here (you won't see OUT printed) and from any triggering action.
## `'
					<action>launch:DIALOG_COVER</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiDialogCoverCheck: OUT\"</action>"
					fi`'
				</menuitem>
				<menuitem checkbox="false">
					<variable>muiCoverPressCheck</variable>
					<input file>'$TEMP_DIR'/coverpresscheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiCoverPressCheck: IN\"</action>"
					fi`'
					<action>refresh:nbkPlayPause</action>
					<action>refresh:muiCoverRefreshCheck</action>
					<action>refresh:muiTrackInfoRefreshCheck</action>
					<action>refresh:muiAutoNextCheck</action>
					<action>refresh:muiDialogCoverCheck</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiCoverPressCheck: OUT\"</action>"
					fi`'
				</menuitem>
				<menuitem checkbox="false">
					<variable>muiFolderOpenCheck</variable>
					<input file>'$TEMP_DIR'/folderopencheck</input>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiFolderOpenCheck: IN\"</action>"
					fi`'
					<action>disable:tmrAutoNext</action>
'`Comment ##
## I cannot split this across multiple lines because it requires double-quotes
## and splitting it into separate actions results in multiple subshells.
## `'
					<action>. funcStop; funcStop; . funcPlaylistCreate; funcPlaylistCreate "$entFolderOpen"; . funcWidgetSchedule; funcWidgetSchedule muiCoverRefreshCheck coverrefreshcheck; funcWidgetSchedule muiTrackInfoRefreshCheck trackinforefreshcheck; . funcPlay; funcPlay</action>
					<action>refresh:nbkPlayPause</action>
					<action>refresh:muiAutoNextCheck</action>
					<action>refresh:muiCoverRefreshCheck</action>
					<action>refresh:muiTrackInfoRefreshCheck</action>
					<action>refresh:entFolderOpen</action>
					'`if [ $DEBUG_TRANSITS -ne 0 ]; then
						echo "<action>echo \"muiFolderOpenCheck: OUT\"</action>"
					fi`'
				</menuitem>
			</menu>
		</menubar>
'`Comment ##
## If the user selects a folder using the folder open button then the path
## will be stored here which will trigger the default signal. This triggering
## will also occur when it is refreshed [to empty it] and so a check widget is
## used to enable the playlist to be created and auto-played conditionally.
## `'
		<entry editable="false" fs-action="folder"
			fs-title="Select a folder to play">
			<variable>entFolderOpen</variable>
			<input file>'$TEMP_DIR'/folderopen</input>
			'`if [ $DEBUG_TRANSITS -ne 0 ]; then
				echo "<action>echo \"entFolderOpen: IN\"</action>"
			fi`'
'`Comment ##
## I cannot split this across multiple lines because it requires double-quotes
## and splitting it into separate actions results in multiple subshells.
## `'
			<action>if [ "$entFolderOpen" != "[empty]" ]; then . funcWidgetSchedule; funcWidgetSchedule muiFolderOpenCheck folderopencheck; fi</action>
			<action>refresh:muiFolderOpenCheck</action>
			'`if [ $DEBUG_TRANSITS -ne 0 ]; then
				echo "<action>echo \"entFolderOpen: OUT\"</action>"
			fi`'
		</entry>
	</vbox>
'`Comment ##
## The window "show" signal is used to start playing the initial track
## and is a good place to auto-start something.
## `'
	<action signal="show">. funcPlay; funcPlay</action>
	<action signal="show">refresh:nbkPlayPause</action>
	<action signal="show">refresh:muiAutoNextCheck</action>
</window>
' > $TEMP_DIR/dialog_main

## Export everything necessary.
export GTKDIALOG
export TARGET
export TITLE
export VERSION
export LOCAL_DATA_DIR
export PACKAGE_DATA_DIR
export DEBUG_CONTENT
export DEBUG_TRANSITS
export COVER_SEARCH_PATTERN
export DIALOG_README_SHOW
export PATH
export TEMP_DIR
export GTK2_RC_FILES
export DIALOG_COVER

## Open the README dialog?
funcDialogReadMeOpen

## And off we go...
$GTKDIALOG --space-expand=true --space-fill=true --file=$TEMP_DIR/dialog_main

## Stop any playing track.
funcStop

## Copy the non-empty playlist* files to the local data directory.
if [ `functmpGet playlistcount` -gt 0 ]; then
	cp $TEMP_DIR/playlist* $LOCAL_DATA_DIR/
fi

## Remove the temporary files.
rm -rf $TEMP_DIR
