#!/bin/bash

shopt -s nocasematch

# Uncomment this line to enable verbose/debug logging
#set -x

INPUTFILE="$1"
OUTPUTFILE="$2"

# application/x-ms-shortcut (.lnk) support.
# Original version by jopka (https://launchpad.net/~jopka), modified to use
# lnkinfo by James Lu
if [[ ${INPUTFILE##*.} = 'lnk' ]]
then
	which winepath >&2 || exit 1

	LNKINFO=$(lnkinfo "$INPUTFILE")

	# First try to get the relative shortcut path, and fall back to the absolute (local) path if that doesn't exist
	RELPATH="$(grep 'Relative path' <<< "$LNKINFO" | sed 's/^.*\: //')"
	if [ -z "$RELPATH" ]
	then
		RELPATH="$(grep 'Local path' <<< "$LNKINFO" | sed 's/^.*\: //')"
	else
		RELPATH="$(dirname "$INPUTFILE")/${RELPATH}"
	fi

	INPUTFILE="$(
		winepath -u "$RELPATH"
	)"

	[ ! "$INPUTFILE" ] && exit 1
	SHORTCUT='TRUE'

fi

if ! TEMPDIR="$(mktemp -d)"
then
	echo "exe-thumbnailer: Failed to make a temporary folder, quitting..." && exit 1
fi
TEMPFILE1="$TEMPDIR/group_icon_res"
TEMPFILE2="$TEMPDIR/extracted_icon.png"
TEMPTHUMB="$TEMPDIR/thumbnail.png"

# Get the current icon theme (or override it by 4th parameter):
case "$XDG_CURRENT_DESKTOP" in
'XFCE')
	ACTIVE_THEME="$(xfconf-query -c xsettings -p /Net/IconThemeName)"
	;;
'Cinnamon'*)
	ACTIVE_THEME="$(gsettings get org.cinnamon.desktop.interface icon-theme)"
	;;
'MATE'*)
	ACTIVE_THEME="$(gsettings get org.mate.interface icon-theme)"
	;;
*) # GNOME, Unity, Budgie, etc.
	ACTIVE_THEME="$(gsettings get org.gnome.desktop.interface icon-theme)"
	;;
esac

eval THEME="${4:-$ACTIVE_THEME}"

if [ ! -z "$XDG_CACHE_HOME" ]
then
	THUMBNAILS_PATH="$XDG_CACHE_HOME/thumbnails"
else
	THUMBNAILS_PATH="$HOME/.cache/thumbnails"
fi
mkdir -p "$THUMBNAILS_PATH"

# Delete previously created thumbnails if icon theme has been changed since last run
if [ -s "$THUMBNAILS_PATH/gnome-exe-thumbnailer.cfg" ] && [ ! "$THEME" = "$(cat "$THUMBNAILS_PATH/gnome-exe-thumbnailer.cfg")" ]
then
	rm "$(cat "$THUMBNAILS_PATH/gnome-exe-thumbnailer.files")"
	true > "$THUMBNAILS_PATH/gnome-exe-thumbnailer.files"
	echo "$THEME" > "$THUMBNAILS_PATH/gnome-exe-thumbnailer.cfg"

fi

echo "$THUMBNAILS_PATH/*/$(echo -n "$3" | md5sum | cut -d' ' -f1).png" \
>> "$THUMBNAILS_PATH/gnome-exe-thumbnailer.files"

case "$THEME" in
	Faience*)
		THEME='faience'
		DRAW='roundRectangle 2,2 45,45 3,3'
	;;

	elementary*|Ubuntu-Mono*|Humanity*)
		THEME='elementary'
		DRAW='roundRectangle 2,2 45,45 3,3'
	;;

	*Faenza*)
		THEME='faenza'
		DRAW='roundRectangle 2,2 45,45 3,3'
	;;

	elementary*|Ubuntu-Mono*|Humanity*)
		THEME='elementary'
		DRAW='roundRectangle 2,2 45,45 3,3'
	;;

	AwOken)
		THEME='awoken'
		DRAW='roundRectangle 3,3 44,44 3,3'
	;;

	AwOkenWhite)
		THEME='awoken-white'
		DRAW='roundRectangle 3,3 44,44 3,3'
	;;

	AwOkenDark)
		THEME='awoken-dark'
		DRAW='roundRectangle 4,4 43,43 3,3'
	;;

	gnome|Human)
		THEME='gnome'
		DRAW='roundRectangle 2,2 45,45 4,4'
	;;

	Breathe)
		THEME='breathe'
		DRAW='roundRectangle 1,2 46,45 2,2'
	;;

	NITRUX-Buttons)
		THEME='nitrux-buttons'
		DRAW='roundRectangle 1,1 46,46 6,6'
	;;

	NITRUX-Buttons-2)
		THEME='nitrux-buttons-2'
		DRAW='roundRectangle 1,1 46,46 6,6'
	;;

	NITRUX*)
		THEME='nitrux'
		DRAW='roundRectangle 2,2 45,45 1,1'
	;;

	Captiva)
		THEME='captiva'
		DRAW='roundRectangle 2,1 45,43 15,15'
	;;

	Paper)
		THEME='paper'
		DRAW='roundRectangle 4,4 43,43 1,1'
	;;

	Square-Beam)
		THEME='square-beam'
		DRAW='roundRectangle 0,0 47,44 1,1'
	;;

	box)
		THEME='box'
		DRAW='roundRectangle 5,5 42,42 1,1'
	;;

	Moka)
		THEME='moka'
		DRAW='roundRectangle 2,3 44,45 8,8'
	;;

	Evolvere-*vivid*)
		THEME='evolvere-vivid'
		DRAW='roundRectangle 0,0 47,47, 1,1'
	;;

	Evolvere)
		THEME='evolvere'
		DRAW='roundRectangle 0,0 47,47, 1,1'
	;;

	Compass)
		THEME='compass'
		DRAW='roundRectangle 3,3 44,44 5,5'
	;;

	Vibrancy*)
		THEME='vibrancy'
		DRAW='roundRectangle 3,3 44,44 8,8'
	;;

	Tango*|*)
		THEME='tango'
		DRAW='roundRectangle 2,2 45,45 4,4'
	;;

esac

# Do not try to generate thumbnails for stuff that isn't a file
# This can otherwise cause the thumbnailer to freeze when trying to parse, for
# example, an .lnk pointing to a hard drive partition
if [ ! -f "$INPUTFILE" ]
then
	exit 1
fi

INPUTFILE_SIZE=$(du -b "$INPUTFILE" | cut -f1 -d$'\t')
# Try to fetch the max. size for thumbnailed executables from gsettings, but fall back to 10485760 bytes if that fails.
THUMBNAIL_LIMIT=$(gsettings get org.gnome.nautilus.preferences thumbnail-limit | cut -f2 -d' ')

if [[ ! "$THUMBNAIL_LIMIT" ]]
then
	THUMBNAIL_LIMIT=10485760
fi


if [[ ${INPUTFILE##*.} = 'msi' ]]
then
	# Use generic installer icon for a .msi package:
	ICON=/usr/share/pixmaps/exe-thumbnailer/$THEME/installer.png
	TUNE='-modulate 120,100,0'

elif [ "$INPUTFILE_SIZE" -lt "$THUMBNAIL_LIMIT" ]
then
	# Extract group_icon resource. If we get the "wrestool: $INPUTFILE could not find `1' in
	# `group_icon' resource." error, there is a 99.9% chance that input file is an installer.

	# Warning: Some redirection magic ahead.

	if wrestool --extract --type=group_icon "$INPUTFILE" 2>&1 >"$TEMPFILE1" \
	| grep "could not find \`1' in \`group_icon' resource"
	then
		# Use generic installer icon:
		ICON=/usr/share/pixmaps/exe-thumbnailer/$THEME/installer.png
		TUNE='-modulate 120'

	else
		# Process extracted data, if we have some:
		if [ -s "$TEMPFILE1" ]
		then
			# Look for the best usable icon. 32x32x32 is the first choice, but sometimes is that icon only
			# an empty box with no visible pixels (e.g. in Simon Tatham's Portable Puzzle Collection).
			# In that case we can try to lower the bit depth and look again.
			for BITDEPTH in 32 24 8 4 1
			do
				read -r OFFSET INDEX < <(
					icotool --list "$TEMPFILE1" | awk '{
						ci=int(substr($2,index($2,"=") + 1));
						cw=int(substr($3,index($3,"=") + 1));
						cb=int(substr($5,index($5,"=") + 1));

						if (((cw > w && cw <= 32) || (cw == w && cb > b)) && cb <= '$BITDEPTH') {
							b = cb;
							w = cw;
							i = ci;
						}
					}
					END {
						print (32 - w) / 2, i;
					}'
				)

				# Use a resized 48x48 icon if 32x32 or smaller isn't available.
				# This is very rare (e.g. peazip.exe), but it happens sometimes:
				if [ "$INDEX" = '' ]
				then
					INDEX=1
					RESIZE=yes
					OFFSET=$((OFFSET - 16))
				fi

				# Finally try to extract chosen icon:
				icotool --extract --index=$INDEX "$TEMPFILE1" -o "$TEMPFILE2"

				if [ -s "$TEMPFILE2" ]
				then
					ICON=$TEMPFILE2
					[ "$RESIZE" ] && mogrify -resize 32x32 "$ICON"

				else
					# This case generally happens when the hi-res icons are in new "Vista" icon format (bunch of compressed PNGs).
					# Icotool from icoutils 0.29.1 supports it already, but is unable to extract the one selected icon only.

					# Try to extract all icons:
					icotool --extract "$TEMPFILE1" -o "$TEMPDIR"

					# There's always a 32x32x32 icon in "Vista" icons, but just to be sure:
					[ -s "${TEMPFILE1}_${INDEX}_32x32x${BITDEPTH}.png" ] && ICON="${TEMPFILE1}_${INDEX}_32x32x${BITDEPTH}.png"

				fi

				if [ "$ICON" ]
				then
					# Verify that the selected icon is not just an empty box:
					if [ "$(convert "$ICON" -filter box -resize 1x1! -format "%[fx:u]" info:)" = '0' ]
					then
						# Take next iteration with lower bit depth
						unset ICON
						continue

					else
						break

					fi

				else
					break

				fi

			done

		fi

	fi

fi


# Create the basic thumbnail:
if [ "$ICON" ]
then
	# Calculate the background color:
	COLOR=$(
		convert "$ICON" -background white -flatten -fill white \
		-fuzz 40% -opaque black -level 33%,66% -scale 1x1! "$TUNE" txt:- \
		| tail -1 \
		| grep -o '#......'
	)

else
	# We failed to fetch a meaningful icon, so exit with a non-zero code and force the file manager
	# to use the theme's default icon.

	# There used to be a fallback icon generation clause here, but I removed that because the output
	# icons don't fit well with most icon themes. Specifically, the output was overly colourful,
	# whereas file icons on most themes (I checked Faenza, Numix, Moka, Oxygen, Tango, Adwaita) are
	# relaxed in the amount of colours they use.
	# (Also, repeating the first two letters of the filename as a label is largely meaningless to
	# end users.)
	exit 1

fi

# Create the final thumbnail:
OFFSET=$((OFFSET + 8))

if [ "$SHORTCUT" ]
then
	# Variant with MS shortcut emblem in bottom left corner for .lnk files
	convert -size 48x48 xc:none -fill "$COLOR" -draw "$DRAW" "$TUNE_NX" miff:- \
	| composite -compose multiply "/usr/share/pixmaps/exe-thumbnailer/$THEME/template.png" - miff:- \
	| composite -geometry "+$OFFSET+$OFFSET" "$ICON" - png:- \
	| composite -gravity southwest /usr/share/pixmaps/exe-thumbnailer/shortcut.png - "$TEMPTHUMB"

else
	# Plain variant
	convert -size 48x48 xc:none -fill "$COLOR" -draw "$DRAW" "$TUNE_NX" miff:- \
	| composite -compose multiply "/usr/share/pixmaps/exe-thumbnailer/$THEME/template.png" - png:- \
	| composite -geometry "+$OFFSET+$OFFSET" "$ICON" - "$TEMPTHUMB"

fi

# Get the version number:
if [[ ${INPUTFILE##*.} = 'msi' ]]
then
	# Look for the ProductVersion property using msitools' msiinfo if present
	if which msiinfo
	then
		VERSION=$(msiinfo export "$INPUTFILE" 'Property' | grep 'ProductVersion' | cut -f 2)
	else
		# Try to get the version number from extended file properties at least:
		VERSION=$(
			file "$INPUTFILE" \
			| grep -o ', Subject: .*, Author: ' \
			| grep -Eo '[0-9]+\.[0-9]+(\.[0-9][0-9]?)?(beta)?' \
			| head -1
		)
	fi

elif [ "$INPUTFILE_SIZE" -lt "$THUMBNAIL_LIMIT" ]
then
	# Extract raw version resource:
	wrestool --extract --raw --type=version "$INPUTFILE" > "$TEMPFILE1"

	if [ -s "$TEMPFILE1" ]
	then
		# Search for a sane version string.
		# This (especially the final regexp) took me really long time to figure out. Am I that lame?
		VERSION=$(< "$TEMPFILE1" \
			tr '\0, ' '\t.\0' \
			| sed 's/\t\t/_/g' \
			| tr -c -d '[:print:]' \
			| sed -r -n 's/.*Version[^0-9]*([0-9]+\.[0-9]+(\.[0-9][0-9]?)?).*/\1/p'
		)
	fi

fi


# Put a version label on the thumbnail:
if [ "$VERSION" ]
then
	convert -font -*-clean-medium-r-*-*-6-*-*-*-*-*-*-* \
	-background '#00001090' -fill white label:"$VERSION" \
	-trim -bordercolor '#00001090' -border 2 \
	-fill '#00001048' \
	-draw $'color 0,0 point\ncolor 0,8 point' -flop \
	-draw $'color 0,0 point\ncolor 0,8 point' -flop \
	miff:- | composite -gravity southeast - "$TEMPTHUMB" "$OUTPUTFILE"
else
	cp "$TEMPTHUMB" "$OUTPUTFILE"
fi

rm -r "$TEMPDIR"
