#!/bin/bash

# uncomment for very verbose script debugging...
#set -x

# Copyright (C) 2012-2016 by Debian Edu project, http://wiki.debian.org/DebianEdu
#       2012-2016, Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
#       2016, Daniel Teichmann <daniel@teichm-sh.de>
#       2022, Dominik George <dominik.george@teckids.org>

# Impressive PDF Display 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.
#
# Impressive PDF Display 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.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

# dependencies:
#     wmctrl
#     matchbox-window-manager
#     pulseaudio-utils
#
# recommendations:
#     unclutter
#     pulseaudio
#     x11-xserver-utils
#     texlive-extra-utils (for pdfxup)
#     poppler-utils (for pdfinfo)

VERSION="0.4.1"

# List of supported extensions taken from impressive
# We add .txt, which is a file list (denoted by an @ prefix in impressive)
SUPPORTED_FILES=(pdf jpg jpeg png tif bmp ppm avi)
if type -p mplayer 1>/dev/null; then
	SUPPORTED_FILES+=(mov mp4 mkv webm ogv mpg mpeg ts flv txt)
fi

# Can be configured at "/etc/default/impressive-display"
DEFAULT_PDF_URI="file:///usr/share/doc/impressive-display/impressive-display.pdf.gz"
DEFAULT_RESOLUTION=""

# all displays that need to be changed
declare -A DISPLAYS

# launch pulseaudio daemon if not already running
WITH_PULSEAUDIO="yes"

# hide idle mouse pointer
HIDE_IDLE_POINTER="yes"

# default screensaver settings
SCREENSAVER_SETTINGS=""

# don't use an HTTP proxy by default
HTTP_PROXY_URL=""

# interval of PDF continuous downloads
DOWNLOAD_INTERVAL="600"

# During rush hours check PDF file on remote URL more often
RUSH_HOURS="07-11"
RUSH_HOURS_DOWNLOAD_INTERVAL="180"

LOG_TO_SYSLOG=

# Disable PDF downloads at night time completely...
SLEEP_HOURS="22-06"
SLEEP_HOURS_DOWNLOAD_INTERVAL="0"

# show each slide for 15 seconds by default
SLIDE_DURATION="15"

# option for impressive fading...
IMPRESSIVE_OPTIONS="--transition None"

# write some debugging output to stdout when downloading a remote PDF
DEBUG_DOWNLOADS=""

if type -p xrandr 1>/dev/null; then
	currentOutput=`echo "$(xrandr)" | grep -w 'connected' | sed -r -e 's/connected.*//' | head -n 1`
fi

if [ -r /etc/default/impressive-display ] && [ ! -d /etc/default/impressive-display ]; then
	. /etc/default/impressive-display
fi

if [ -r $HOME/.impressive-display/config ] && [ ! -d $HOME/.impressive-display/config ]; then
	. $HOME/.impressive-display/config
fi

if [ "x$LOG_TO_SYSLOG" = "xyes" ]; then
	OUTPUT="logger -t impressive-display"
else
	OUTPUT="echo"
fi

workdir="$HOME/.impressive-display"
mkdir -p "$workdir/downloaded" "$workdir/display"

session_lock="$workdir/.session-lock.$$"
impressive_lock="$workdir/.impressive-lock"

# provide pulseaudio support in the impressive display session, if not
# already available...
if ! pacmd stat 1>/dev/null 2>/dev/null; then
	if [ "x$WITH_PULSEAUDIO" = "xyes" ]; then
		if which pulseaudio 1>/dev/null; then
			pulseaudio -D -n \
			           -L 'module-udev-detect' \
			           --exit-idle-time=65535
		fi
	fi
fi

# launch matchbox manager
if ! wmctrl -m 1>/dev/null 2>/dev/null; then
	if which matchbox-window-manager 1>/dev/null; then
		matchbox-window-manager 1>/dev/null 2>/dev/null&
	fi
fi

# use unclutter to hide idle mouse pointers
if [ "x$HIDE_IDLE_POINTER" = "xyes" ]; then
	if which unclutter 1>/dev/null; then
		unclutter 1>/dev/null 2>/dev/null &
	fi
fi

# if FAKE_HOSTNAME is not empty, use that string for hostname
THIS_HOSTNAME=$(hostname -f)
if [ -n "$FAKE_HOSTNAME" ]; then
	THIS_HOSTNAME="$FAKE_HOSTNAME"
fi


if `echo ${!DISPLAYS[@]} | grep -q "$THIS_HOSTNAME"`; then 
	$OUTPUT "INFO: $THIS_HOSTNAME has extra properties. loading them.."

	if echo ${DISPLAYS[$THIS_HOSTNAME]} | grep -q "pdf_uri="; then
		PDF_URI=`echo ${DISPLAYS[$THIS_HOSTNAME]} | sed -r -e 's/(^|.*\|)pdf_uri=([^|]*).*/\2/'`
	else
		PDF_URI="$DEFAULT_PDF_URI"
	fi

	if echo ${DISPLAYS[$THIS_HOSTNAME]} | grep -q "res="; then
		RESOLUTION=`echo ${DISPLAYS[$THIS_HOSTNAME]} | sed -r -e 's/(^|.*\|)res=([^|]*).*/\2/'`
	else
		RESOLUTION="$DEFAULT_RESOLUTION"
	fi
else
	PDF_URI="$DEFAULT_PDF_URI"
	RESOLUTION="$DEFAULT_RESOLUTION"
fi

# Verify that URI is of a supprted form
function uri_is_supported {
	check_uri=${1%%\?*}

	if ! echo "$check_uri" | grep -q -E "^(file://|http://|https://).*"; then
		$OUTPUT "ERROR: PDF_URI format not supported: $check_uri"
		$OUTPUT "       Use file://<path>/<file>(.gz) or http(s)://<host>/<path>/<file>. Doing nothing."
		$OUTPUT "       Supported file types are: ${SUPPORTED_FILES[@]}"
		return -1
	fi
}

# Verify primary URL; bail out if it is unsupported
uri_is_supported "$PDF_URI" || exit -1

if [ -n "${RESOLUTION}" ]; then
	if type -p xrandr 1>/dev/null; then
		xrandr -d :0 --output "$(echo $currentOutput)" --mode $RESOLUTION
	fi
fi

# Set screensaver settings
if which xset 1>/dev/null; then
	xset s ${SCREENSAVER_SETTINGS}
fi

# set some proxy related env variables, if requested...
if [ -n "$HTTP_PROXY_URL" ]; then
	export http_proxy="$HTTP_PROXY_URL"
	export https_proxy="$HTTP_PROXY_URL"
	export ftp_proxy="$HTTP_PROXY_URL"
fi

rush_hours_start=`echo $RUSH_HOURS | grep -E '^[0-9]{2}-[0-9]{2}$' | sed  -n -r -e 's/^([0-9]{2})-([0-9]{2})$/\1/p'`
rush_hours_end=`echo $RUSH_HOURS | grep -E '^[0-9]{2}-[0-9]{2}$' | sed  -n -r -e 's/^([0-9]{2})-([0-9]{2})$/\2/p'`
sleep_hours_start=`echo $SLEEP_HOURS | grep -E '^[0-9]{2}-[0-9]{2}$' | sed  -n -r -e 's/^([0-9]{2})-([0-9]{2})$/\1/p'`
sleep_hours_end=`echo $SLEEP_HOURS | grep -E '^[0-9]{2}-[0-9]{2}$' | sed  -n -r -e 's/^([0-9]{2})-([0-9]{2})$/\2/p'`

function cleanup {
	if type -p xrandr 1>/dev/null; then
		$OUTPUT "Info: resetting resolution"
		xrandr -d :0 --output "$(echo $currentOutput)" --auto
	fi

	if [ -r "$impressive_lock" ]; then
		impressive_pid=$(cat "$impressive_lock" | sed -e 's/[^0-9]*//g')
		if [ -n "$impressive_pid" ]; then
			kill -0 $impressive_pid 2>/dev/null && kill "$impressive_pid"
		fi
	fi

	for rmfile in $impressive_lock \
	              $session_lock \
	              $workdir/downloaded/ \
	              $workdir/display/ \
	              ;
	do
		if [ -e "$rmfile" ]; then
			rm -r "$rmfile"
		fi
	done

}
trap "cleanup" SIGINT SIGTERM ERR EXIT

function pdf_is_portrait {

	if ! which bc 1>/dev/null; then
		$OUTPUT "WARNING: The 'bc' command line tool is not installed on this system."
		return -1
	fi

	check_pdffile=$1
	if [ -n "$check_pdffile" ] && [ -r "$check_pdffile" ]; then

		pdfinfo \
		    -f 1 \
		    -l 1000 \
		 ${check_pdffile} \
		| grep "Page.* size:" \
		| while read Page _pageno size _width x _height rest; do 
			if [ "$(echo "${_width} / 1"|bc)" -gt "$(echo "${_height} / 1"|bc)" ]; then
			    let num_of_landscape_pages=num_of_landscape_pages+1 # Page is landscape...
			    echo "PAGE_IS_LANDSCAPE"
			    break
			fi
		done | tail -n1 | grep -q -E ".*PAGE_IS_LANDSCAPE.*" && return 1
		return 0
	else
		# something went wrong with the provided file name...
		return -1
	fi

}

function merge_portrait_documents {
	local pdffile=$1

	if which pdfxup 1>/dev/null ; then
		if pdf_is_portrait "$pdffile"; then
			cd $(dirname "$pdffile") && pdfxup "$pdffile" 1>/dev/null && cd - 1>/dev/null
			mv $(dirname "$pdffile")/pdfxup.pdf "$pdffile"
		fi
	else
		$OUTPUT "WARNING: The pdfnup tool is not installed."
	fi

}

function is_rush_hour {

	if [ -n "$rush_hours_start" ] && [ -n "$rush_hours_end" ]; then

		current_hour=`date +%H`
		if [ $rush_hours_end -gt $rush_hours_start ]; then

			if [ $rush_hours_start -le $current_hour  ] && [ $current_hour -lt $rush_hours_end ]; then
				echo "true"
				return 0
			fi

		else

			if [ $rush_hours_end -le $current_hour  ] && [ $current_hour -lt $rush_hours_start ]; then
				:
			else
				echo "true"
				return 0
			fi

		fi

	fi
	echo "false"
	return -1

}

function is_sleep_hour {

	if [ -n "$sleep_hours_start" ] && [ -n "$sleep_hours_end" ]; then

		current_hour=`date +%H`
		if [ $sleep_hours_end -gt $sleep_hours_start ]; then

			if [ $sleep_hours_start -le $current_hour  ] && [ $current_hour -lt $sleep_hours_end ]; then
				echo "true"
				return 0
			fi

		else

			if [ $sleep_hours_end -le $current_hour  ] && [ $current_hour -lt $sleep_hours_start ]; then
				:
			else
				echo "true"
				return 0
			fi

		fi

	fi
	echo "false"
	return -1

}

# Download a file (any file, not only PDF, actually)
function download_pdffile {
	# Params: full download URL; and indicator wif we are loading
	#  primary file or subsequent list entries
	local pdf_uri_orig=$1
	local recursing=${2:-no}

	# Split/strip query string filename, uncompressed filename, and extension
	local pdf_uri=${pdf_uri_orig%%\?*}
	local pdf_filename=$(basename "$pdf_uri")
	local pdf_stripped=${pdf_filename%.gz}
	local pdf_extension=${pdf_stripped##*.}

	# Full paths for new file, and previous version currently displayed
	local pdffile_downloaded="$workdir/downloaded/$pdf_stripped"
	local pdffile_target="$workdir/display/$pdf_stripped"

	# Do actual download using curl
	DOWNLOADED_FILE="$workdir/downloaded/$pdf_filename"
	if [ -f "$workdir/secrets/default.secret" ]; then
		NETRCFILE=" --netrc-file $workdir/secrets/default.secret"
	fi
	if [ -f "$workdir/secrets/$THIS_HOSTNAME.secret" ]; then
		NETRCFILE=" --netrc-file $workdir/secrets/${THIS_HOSTNAME}.secret"
	fi
	if [ "x$LOG_TO_SYSLOG" = "xyes" ]; then
		if ! curl -f $NETRCFILE "$pdf_uri_orig" 1> "$DOWNLOADED_FILE" 2>/dev/null; then
			$OUTPUT "The curl tool failed in retrieving PDF file. Run this tool interactively to debug problems."
			return
		fi
	else
		if ! curl -f $NETRCFILE "$pdf_uri_orig" 1> "$DOWNLOADED_FILE"; then
			$OUTPUT "The curl tool failed in retrieving PDF file."
			return
		fi
	fi

	# is the file gzipped?
	if echo "$DOWNLOADED_FILE" | grep -q -E '.*\.gz$'; then
		gunzip -f "$DOWNLOADED_FILE"
	fi

	# Treat special cases
	if [[ $pdf_extension = txt ]]; then
		# This is a list file - read it and recurse
		while read -r line; do
			if echo "$line" | grep -q -E '^#'; then
				# Simply pass through comments
				echo "$line"
			else
				next_uri=${line%%\#*}
				next_uri=${next_uri%% *}

				if uri_is_supported "$next_uri"; then
					# This will output the stripped filename / path
					download_pdffile "$next_uri" yes
				fi
			fi
		done <"$pdffile_downloaded" >"$pdffile_downloaded.new"
		mv "$pdffile_downloaded.new" "$pdffile_downloaded"
	elif [[ $pdf_extension = pdf ]] && pdfinfo "$pdffile_downloaded" 1>/dev/null 2>/dev/null; then
		# This is a PDF file - merge portrait documents
		merge_portrait_documents "$pdffile_downloaded"
	fi

	# If this is the top-level download call, compare the two directories
	if [[ $recursing = no ]]; then
		local reload=no

		# Check for files that are in display/ but not in downloaded/
		for file in "$workdir/"display/*; do
			[[ $(basename "$file") = '*' ]] && break

			if ! [[ -e "$workdir/downloaded/$(basename "$file")" ]]; then
				# File is no longer desired, remove and trigger reload
				rm "$file"
				reload=yes
			fi
		done

		# Compare files in downloaded/ to files in display/
		for file in "$workdir/"downloaded/*; do
			[[ $(basename "$file") = '*' ]] && break

			cmp -s "$file" "$workdir/display/$(basename "$file")"> /dev/null
			if [ $? -ne 0 ]; then
				# File is new or changed, copy and trigger reload
				mv "$file" "$workdir/display/$(basename "$file")"
				reload=yes
			fi
		done

		if [[ $reload = yes ]]; then
			$OUTPUT "INFO: Triggering PDF file reload, set of files has changed..."
			echo -n "RELOAD" > $impressive_lock
		fi
	fi

	if [[ $recursing = yes ]]; then
		# Is this a recursive list?
		if [[ $pdf_extension = txt ]]; then
			# Output list of all filenames so we get a concatenated final list
			cat "$pdffile_target"
		else
			# Print only the single filename for the parent call to grab
			echo "$pdffile_target"
		fi
	else
		# Is this a list file? Prepend @
		if [[ $pdf_extension = txt ]]; then
			pdffile_display=@$pdffile_target
		else
			pdffile_display=$pdffile_target
		fi
	fi
}

function download_loop {

	# do an initial PDF retrieval..
	download_pdffile "$PDF_URI"

	if [ "x$DOWNLOAD_INTERVAL" != "x0" ]; then
		(
			while [ -e "$session_lock" ]; do

				if [ -n "$DEBUG_DOWNLOADS" ]; then
					$OUTPUT "DEBUG: rush hour: `is_rush_hour` (interval: $RUSH_HOURS_DOWNLOAD_INTERVAL), sleep hour: `is_sleep_hour` (interval: $SLEEP_HOURS_DOWNLOAD_INTERVAL). Normal interval: $DOWNLOAD_INTERVAL."
				fi

				if is_rush_hour 1>/dev/null && [ "x$RUSH_HOURS_DOWNLOAD_INTERVAL" != "x0" ]; then
					sleep $RUSH_HOURS_DOWNLOAD_INTERVAL
					if [ -n "$DEBUG_DOWNLOADS" ]; then
						$OUTPUT "INFO: RUSH HOUR download, URL: $PDF_URI"
					fi
					download_pdffile "$PDF_URI"
				elif is_sleep_hour 1>/dev/null  && [ "x$SLEEP_HOURS_DOWNLOAD_INTERVAL" != "x0" ]; then
					sleep $SLEEP_HOURS_DOWNLOAD_INTERVAL
					if [ -n "$DEBUG_DOWNLOADS" ]; then
						$OUTPUT "INFO: SLEEP HOUR download, URL: $PDF_URI"
					fi
					download_pdffile "$PDF_URI"
				elif (! is_rush_hour 1>/dev/null ) && (! is_sleep_hour 1>/dev/null) && [ "x$DOWNLOAD_INTERVAL" != "x0" ]; then
					sleep $DOWNLOAD_INTERVAL
					if [ -n "$DEBUG_DOWNLOADS" ]; then
						$OUTPUT "INFO: NORMAL HOUR download, URL: $PDF_URI"
					fi
					download_pdffile "$PDF_URI"
				else
					if [ -n "$DEBUG_DOWNLOADS" ]; then
						$OUTPUT "DEBUG: Not doing any download."
					fi
					sleep 60
				fi
			done
		) &
	fi

}

function reloadable_impressive {

	if which impressive 1>/dev/null; then

		while [ -e "$session_lock" ]; do

			# FIXME: display error msg if no PDF file could be retrieved

			$OUTPUT "INFO: Starting a new instance of the impressive application."
			impressive ${IMPRESSIVE_OPTIONS} --fake-fullscreen --wrap -a ${SLIDE_DURATION} --nologo "${pdffile_display}" 1>/dev/null 2>/dev/null &
			impressive_pid=$!

			echo -n "$impressive_pid" > "$impressive_lock"

			while [ -r $impressive_lock ] && [ "$(cat $impressive_lock)" != "RELOAD" ] && [ -e "$session_lock" ]; do
				sleep 1
				if ! kill -0 "$impressive_pid" 2>/dev/null; then
					rm "$session_lock"
					break
				fi
			done

			kill -0 "$impressive_pid" 2>/dev/null && kill "$impressive_pid"
			$OUTPUT "INFO: The impressive application has terminated."

		done

	else
		$OUTPUT "ERROR: The impressive application is not installed."
	fi

}


function create_lock {

	touch "$session_lock"

}

### MAIN ###

$OUTPUT "INFO: IMPRESSIVE Display (version $VERSION)"
$OUTPUT "INFO: HOSTNAME set to: $THIS_HOSTNAME"

if [ -n "$PDF_URI" ]; then

	$OUTPUT "INFO: PDF_URI is configured. Using PDF file from given URL: $PDF_URI"
	create_lock
	download_loop

else

	$OUTPUT "ERROR: PDF_URI hasn't been configured. Doing nothing."
	exit 0

fi

reloadable_impressive

exit 0
