#!/bin/sh

set -eu

ROWS=2
COLS=6
ORIENTATION='horizontal'

debug=0

err()
{
	local fmt="${1}"
	shift 1

	printf "Error: ${fmt}\n" "${@}" 1>&2
	return 0
}

dbg()
{
	local fmt="${1}"
	shift 1

	case "${debug}" in '1')
		printf "DEBUG: ${fmt}\n" "${@}" 1>&2
	esac

	return 0
}

get_desktop()
{
	local dt=

	dt=$(wmctrl -d | sed -n '/^[0-9][0-9]*  *\* /{ s/  *\* .*$//; p; q; };')
	case "${dt}" in
		*[!0-9]*)
			err 'Failed to get current desktop number'
			return 1
			;;
	esac

	dbg 'Old desktop: %d' ${dt}

	printf '%d' ${dt}
	return 0
}

inc_desktop()
{
	local dt=${1}
	local dx=${2}
	local dy=${3}

	case "${ORIENTATION}" in
		[Hh]*)
			x=$((${dt} % ${COLS}))
			y=$((${dt} / ${COLS}))
			dbg 'Old coordinates: (%d, %d)' ${x} ${y}
			x=$(((${x} + ${COLS} + ${dx}) % ${COLS}))
			y=$(((${y} + ${ROWS} + ${dy}) % ${ROWS}))
			dbg 'New coordinates: (%d, %d)' ${x} ${y}
			dt=$((${y} * ${COLS} + ${x}))
			;;
		[Vv]*)
			x=$((${dt} / ${ROWS}))
			y=$((${dt} % ${ROWS}))
			x=$(((${x} + ${dx}) % ${COLS}))
			y=$(((${y} + ${dy}) % ${ROWS}))
			dt=$((${x} * ${ROWS} + ${y}))
			;;
	esac

	printf '%d' ${dt}

	return 0
}

set_desktop()
{
	local dt=${1}
	shift 1

	dbg 'New desktop: %d' ${dt}

	wmctrl -s ${dt}

	return 0
}

move_window()
{
	local dt=${1}
	shift 1

	dbg 'New desktop: %d' ${dt}

	wmctrl -r ':ACTIVE:' -t ${dt}
	wmctrl -s ${dt}

	return 0
}

usage()
{
	printf 'Usage: %s [<options>] <direction>\n' "${0}"
	printf '\n'
	printf 'Where <direction> is any word beginning with (in any case):\n'
	printf '  * "U" for up\n'
	printf '  * "D" for down\n'
	printf '  * "L" for left\n'
	printf '  * "R" for right\n'
	printf '\n'
	printf 'Options:\n'
	printf '  -w  Move the active window\n'
	printf '  -d  Enable debugging output\n'

	return 0
}

main()
{
	local move_win=
	local opt=
	local dir=
	local dx=
	local dy=
	local cur_dt=
	local new_dt=

	move_win=0

	while getopts 'wd' opt "${@}"; do
		case "${opt}" in
			'w') move_win=1;;
			'd') debug=1;;
			'?')
				usage 1>&2
				return 1
				;;
		esac
	done
	shift $((${OPTIND} - 1))

	if [ ${#} -ne 1 ]; then
		usage 1>&2
		return 1
	fi

	dir="${1}"
	shift 1

	case "${dir}" in
		[Uu]*) dx=0  dy=-1;;
		[Dd]*) dx=0  dy=1 ;;
		[Ll]*) dx=-1 dy=0 ;;
		[Rr]*) dx=1  dy=0 ;;
		*)
			usage 1>&2
			return 1
			;;
	esac

	cur_dt=$(get_desktop) || return 1
	new_dt=$(inc_desktop ${cur_dt} ${dx} ${dy})
	case ${move_win} in
		1) move_window ${new_dt};;
		0) set_desktop ${new_dt};;
	esac

	return 0
}

main "${@}"