#!/bin/bash

#       -------------------------------------------------------------------
#
#	leds version 0.2
#	leds -- makes *amazing* patterns with the keyboard
#	LEDs! Hours of fun for the whole family!
#	Amaze your friends!
#	Se README file included for a list of valid options
#
#	Copyright  2003, Johannes H. Jensen
#	<joh@deworks.net>
#
#
#	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 2 of the License, or
#	(at your option) any later version.
#
#	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.
#
#	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#       -------------------------------------------------------------------

#       -------------------------------------------------------------------
#	Patterns
#       -------------------------------------------------------------------

clearall="-num -caps -scroll"	# use this var in $patterns to clear all leds if you like.
setall="+num +caps +scroll"	# use this var in $patterns to set all leds if you like.

# OK, here comes the pattern list!
patterns=("+num; -num"
	  "+caps; -caps"
	  "+scroll; -scroll"
	  "$setall; $clearall"
	  "+num; +caps; +scroll; -num; -caps; -scroll"
	  "+scroll; +caps; +num; -scroll; -caps; -num"
	  "+num; +caps; +scroll; -scroll; -caps; -num"
	  "+num; -num; +caps; -caps; +scroll; -scroll"
	  "+scroll; +caps; +num; +num; +num; +num; -scroll; -caps; -num"
	  "+num +caps; -num -caps; +num +caps; -num -caps; +caps +scroll; -caps -scroll; +caps +scroll; -caps -scroll"
	  "$clearall +num; -num; +num; -num; +num; +caps; -caps; +caps; -caps; +caps; +scroll; -scroll; +scroll; -scroll; +scroll; +scroll; +scroll"
	  "$clearall +num; -num; +num; -num; +num; -num; +num; +num; +num +caps; -num -caps; +num +caps; -num -caps; +num +caps; -num -caps; +num +caps; +num; $setall; $clearall; $setall; $clearall; $setall; $clearall; $setall; $clearall; $setall; $clearall; $setall; $clearall; $setall; $clearall; $setall; -scroll; -caps; -num"
	  "$setall -caps; +caps; -caps; +caps; -caps; +caps; -caps; +caps; -num -scroll; +num +scroll; -num -scroll; +num +scroll; -num -scroll; +num +scroll; -num -scroll; +num +scroll $clearall"
	  "$clearall; +num; +num; -num; -num; +num; +num; -num; -num; +scroll; +scroll; -scroll; -scroll; +num; -num; +scroll; -scroll; +num; -num; +scroll; -scroll; +num; +num; -num; -num; +scroll +scroll; $clearall; $clearall; $setall; $setall; $clearall"
	  "+scroll -caps -num; -scroll +caps -num; -scroll -caps +num")


#       -------------------------------------------------------------------
#	Constants
#       -------------------------------------------------------------------

PROGNAME=$(basename $0)
PROGDESCR="Make *amazing* patterns with the keyboard LEDs.\nHours of fun for the whole family.\nAmaze your friends!"
VERSION="0.2"


#       -------------------------------------------------------------------
#	Initialization variables ("default")
#       -------------------------------------------------------------------

secs=0.2		# default seconds between each ledchange
pattern_id=0 		# default pattern_id
verbose=0		# do not verbose by default
setleds="setleds"	# use setleds by default
nums=-1			# default number to repeat the pattern (-1 is infinite)


#       -------------------------------------------------------------------
#	Functions
#       -------------------------------------------------------------------


#	-------------------------------------------------------------------
#	error_exit -- Function for exit due to fatal program error
#	Accepts 1 argument:
#			string containing descriptive error message.
#       -------------------------------------------------------------------

function error_exit
{
	echo -e "$PROGNAME: ${1:-"Unknown error."}" >&2
	usage
	echo "Try \`$PROGNAME' --help for more information"
	exit 1
}


#	-----------------------------------------------------------------------
#	signal_exit -- Function to handle termination signals
#		Accepts 1 argument:
#			signal_spec
#	-----------------------------------------------------------------------

function signal_exit
{
        case $1 in
                INT)    echo "$PROGNAME: Program aborted by user." >&2
                        exit
                        ;;
                TERM)   echo "$PROGNAME: Program terminated." >&2
                        exit
                        ;;
                *)      error_exit "$PROGNAME: Terminating on unknown signal."
                        ;;
        esac
}


#       -------------------------------------------------------------------
#	parameter_error -- Function to return error message for arguments
#			   with too few parameters.
#		Accept two arguments:
#			1: Argument name
#			2: Missing parameter name.
#       -------------------------------------------------------------------

function parameter_error
{
	echo "$1 expects an additional parameter: $2"
}


#       -------------------------------------------------------------------
#	progname -- Function to output program name and version
#		No arguments
#       -------------------------------------------------------------------

function progname
{
	echo "$PROGNAME $VERSION"
}


#	-----------------------------------------------------------------------
#	usage -- Function to display usage message 
#		No arguments
#	-----------------------------------------------------------------------

function usage
{
	echo "Usage: $PROGNAME [-s <seconds>] [-p <id>] [-n <number>] [-v] [-X] [-a <on|off>]"
}


#       -----------------------------------------------------------------------
#       helptext -- Function to display help message for program
#	No arguments
#       -----------------------------------------------------------------------

function helptext
{
	local tab=$(echo -en "\t\t")
	
	cat <<- -EOF-
	$(progname)
	$(echo -en "${PROGDESCR}")

	$(usage)

	Options:

	-h, --help	Display this help message and exit.
	-l, --list	List all patterns with their IDs.
	-p <id>		ID of pattern to use. (Defaults to ${pattern_id})
	-s <seconds>	Number of seconds between each step
			${tab}in the pattern. (Defaults to ${secs})
	-n <number>	Number of time the pattern repeats.
			${tab}-1 is infinite. (Defaults to ${nums})
	-v		Verbose output (output what leds are
			${tab}turned on/off at all times.)
	-X		Use xsetleds instead of setleds.
			${tab}Usefull under X.
	-a <on|off>	Set all the leds on or off.
	--version	Display version invormation and exit.

-EOF-
}


#       -----------------------------------------------------------------------
#       get_patterns -- Function to display the list of valid patterns
#		No arguments
#       -----------------------------------------------------------------------

function get_patterns
{
	progname
	echo "Patterns with their IDs:"
	echo -e "ID\tPattern"
	for (( i=0; $i < ${#patterns[@]}; i++ )); do
	    	echo -e "$i\t${patterns[$i]}"
	done
	echo "End of list"
}


#       -------------------------------------------------------------------
#	check_env -- Function to check the system environment for requred
#		     programs, status, etc.
#		No arguments
#       -------------------------------------------------------------------

function check_env
{
	### xsetleds check ###
	if [ "$setleds" == "xsetleds" ]; then
		if [ -z "$DISPLAY" ]; then
			# we are using xsetleds, but there is no running in X server. Abort.
			error_exit "You have chosen to run leds in X, but I cannot find any X-server running! Aborting."
		fi
		if ! xsetleds > /dev/null 2>&1; then
			# cannot find the xsetleds program, Abort.
			error_exit "I cannot find xsetleds!\nMake sure the directory containing xsetleds is in your \$PATH variable.\nYou can get xsetleds at ftp://ftp.unix-ag.org/user/bmeurer/xsetleds/"
		fi
	elif [ -n "$DISPLAY" ]; then
		# we are not running in a VT, and the setleds wont work. Abort.
		error_exit "This script can only be run in a VT! Aborting.\n(use -X if you want to run $PROGNAME in X.)"
	fi
}


#       -------------------------------------------------------------------
#	Main program
#       -------------------------------------------------------------------

# Trap TERM, HUP, and INT signals and properly exit

trap "signal_exit TERM" TERM HUP
trap "signal_exit INT"  INT

# Command line processing

while (( $# )); do
	case $1 in
		-h|--help) 	helptext
				exit
				;;
		-l|--list)	get_patterns
				exit
				;;
		-p)		if [ -z "$2" ]; then
					error_exit "$( parameter_error $1 "id" )"
				fi
				if [ -z "${patterns[$2]}" ]; then
					error_exit "unrecognized pattern ID: $2\nTry $PROGNAME -l for a full list of patterns and IDs"
				fi
				pattern_id=$2
				shift
				;;
		-s)
				if [ -z "$2" ]; then
					error_exit "$( parameter_error $1 "seconds" )"
				fi
				secs=$2
				shift
				;;
		-n)		if [ -z "$2" ]; then
					error_exit "$( parameter_error $1 "number" )"
				fi
				nums=$2
				shift
				;;
		-v)		verbose=1
				;;
		-X)		setleds="xsetleds"
				;;
		-a)		if [ -z "$2" ]; then
					error_exit "$( parameter_error $1 "on|off" )"
				fi
				if [ "$2" != "on" -a "$2" != "off" ]; then
					error_exit "invalid parameter $2 for option $1. We were expecting on|off."
				fi
				check_env
				progname
				case $2 in
					on)
						eval "$setleds $setall"
						exit
					;;
					off)
						eval "$setleds $clearall"
						exit
					;;
				esac
				shift
				;;
		--version)
				progname
				exit
				;;
		*)
				error_exit "unrecognized option \`$1'"
				;;
	esac
	shift
done


#       -------------------------------------------------------------------
#	Fire up the lights!
#       -------------------------------------------------------------------

# Check environment
check_env

# Output program name and version
progname

for ((; nums > 0 || nums < 0; nums-- )); do
	IFS=";"
	for leds in ${patterns[$pattern_id]}; do
		if [ $verbose == 1 ]; then
			echo "$leds"
		fi
		eval "$setleds $leds"
		sleep $secs
	done
done
