#!/bin/bash

#
# easyGG - an automatic gallery generator
#
# Copyright (c) 2018 Stephan Uhlmann <su@su2.info>
#
# /************************************************************************
# * 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 3 of the License, or     *
# * (at your option) any later version.                                   *
# *                                                                       *
# * See http://www.gnu.org/licenses/gpl.txt for the full text of the GPL. *
# ************************************************************************/
#

#### Default parameters

# Tools needed
# specify full path when not in $PATH
JHEAD="jhead"
CONVERT="convert"
MKDIR="mkdir"
FIND="find"
GREP="grep"
SED="sed"
BASENAME="basename"
DU="du"
SORT="sort"


# title of each page
# can be changed with --title option
TITLE="$(${BASENAME} "$(pwd)")"

# html which will be added on top of the page
# can be changed with --headerhtml option
HEADERHTML=""

# working directory
# all files will be put in a subdirectory of this name
# can be changed with --subdir option
SUBDIR="gallery"

# 0=debug (lots of info for debugging)
# 1=info (default, normal progress info)
# 2=error (program can continue)
# 3=critical (program can't continue)
# can be changed with --verbose and --qiet option
VERBOSELEVEL=1

# max dimension of thumbnail image
# no side (width/height) will be longer than this. but image won't be enlarged
# if it is smaller. aspect ratio will be kept.
# can be changed with the --thumb-dimension option
THUMB_DIM="160"

# max dimension of full image
# no side (width/height) will be longer than this. but image won't be enlarged
# if it is smaller. aspect ratio will be kept.
# can be changed with the --full-dimension option
FULL_DIM="800"



#### Functions

# $1 file
htmlclear()
{
	msg 0 "Creating HTML file for ${1}."
	echo -n > "${SUBDIR}/${1}.html" || msg 3 "Error creating HTML file. Stop."
}

# $1 string, $2 file
htmlprint()
{
	echo -e -n "${1}" >> "${SUBDIR}/${2}.html" || msg 3 "Error writing HTML file. Stop."
}

# $1 title, $2 file
htmlheader()
{
	msg 0 "Writing HTML header for ${2}."
	htmlprint "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\\n" "${2}"
	htmlprint "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\\n" "${2}"
	htmlprint "<head>\\n" "${2}"
	htmlprint "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\\n" "${2}"
	htmlprint "<meta http-equiv=\"Generator\" content=\"easyGG\"/>\\n" "${2}"
	htmlprint "<title>${1}</title>\\n" "${2}"
	htmlprint "<style type=\"text/css\">\\n" "${2}"
	htmlprint "<!--\\n" "${2}"
	htmlprint "  body { color: #000000; background-color: #dddddd; font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; font-size: 15px; }\\n" "${2}"
	htmlprint "  a:link { color: #000000; text-decoration: none; }\\n" "${2}"
	htmlprint "  a:active { color: #000000; text-decoration: none; }\\n" "${2}"
	htmlprint "  a:visited { color: #222222; text-decoration: none; }\\n" "${2}"
	htmlprint "  a:hover { color: #000000; background-color: #bbbbbb; text-decoration: none; }\\n" "${2}"
	htmlprint "  img { border: 1px solid black; }\\n" "${2}"
	htmlprint "  span.hide { visibility: hidden; }\\n" "${2}"
	htmlprint "  div.main { display: table; margin: 0 auto 0 auto; text-align: center; }\\n" "${2}"
	htmlprint "  div.footer { display: block; clear: left; padding-top: 96px; color: #444444; font-size: small; }\\n" "${2}"
	htmlprint "-->\\n" "${2}"
	htmlprint "</style>\\n" "${2}"
	htmlprint "</head>\\n" "${2}"
	htmlprint "<body>\\n" "${2}"
	htmlprint "${HEADERHTML}\\n" "${2}"
}

# $1 file
htmlfooter()
{
	msg 0 "Writing HTML footer for ${1}."
	htmlprint "<div class=\"footer\">\\n" "${1}"
	htmlprint "</div>\\n" "${1}"
	htmlprint "</body>\\n" "${1}"
	htmlprint "</html>\\n" "${1}"
}

# $1 severity, $2 message
# |
# `-> 0=debug,1=info,2=error,3=critical
msg()
{
	if [ "${1}" -ge "${VERBOSELEVEL}" ]; then echo "${2}"; fi
	if [ "${1}" -ge 3 ]; then exit 1; fi
}

# $1 tool
toolcheck()
{
	X=$(type -p "${1}")
	if test "${X}" && test -x "${X}";
		then return 0
		else return 1
	fi
}


usage()
{
	cat << EOF
Usage: easygg [OPTIONS]

This program can be called without any options. It will generate a
subdirectory with a ready to use gallery.
You can override some default options though.

  -t,  --title       change title of gallery
                     (default is current directory name)
  -s,  --subdir      subdirectory in which the gallery files are written
                     (default is "${SUBDIR}")
  -td, --thumb-dimension
                     max dimension of thumbnail image
                     (in pixel, default is ${THUMB_DIM})
                     no side (width/height) will be longer than this. but
                     image won't be enlarged if it is smaller.
  -fd, --full-dimension
                     max dimension of full image
                     (in pixel, default is ${FULL_DIM})
                     no side (width/height) will be longer than this. but
                     image won't be enlarged if it is smaller.
  -H,  --header      HTML code to be inserted on top of each page
                     (default: none)
  -v,  --verbose     show more debugging info
  -q,  --quiet       show only errors
  -h,  --help        shows this information

EOF
}




#### Main


# command line arguments
while [ "${#}" -gt 0 ] ;
do
	case "${1}" in
		--title|-t)
			TITLE="${2}"
			shift
		;;
		--subdir|-s)
			SUBDIR="${2}"
			shift
		;;
		--thumb-dimension|-td)
			THUMB_DIM="${2}"
			shift
		;;
		--full-dimension|-fd)
			FULL_DIM="${2}"
			shift
		;;
		--header|-H)
			HEADERHTML="${2}"
			shift
		;;
		--verbose|-v)
			VERBOSELEVEL=0
		;;
		--quiet|-q)
			VERBOSELEVEL=2
		;;
		--help|-help|-h)
			usage
			exit 0
		;;
		*)
			msg 3 "Unknown option: ${1}"
	;;
	esac
	shift
done


# All tools there?
toolcheck ${JHEAD} || msg 3 "jhead not found."
toolcheck ${CONVERT} || msg 3 "convert not found."
toolcheck ${MKDIR} || msg 3 "mkdir not found."
toolcheck ${FIND} || msg 3 "find not found."
toolcheck ${GREP} || msg 3 "grep not found."
toolcheck ${SED} || msg 3 "sed not found."
toolcheck ${BASENAME} || msg 3 "basename not found."
toolcheck ${DU} || msg 3 "du not found."
toolcheck ${SORT} || msg 3 "sort not found."


# find files and map them into an array
mapfile -t FILES < <(${FIND} . -maxdepth 1 \( -iregex ".*\\.jpe?g$" -o -iregex ".*\\.png$" \) -type f -print | ${SORT} -f)

if [ ${#FILES[@]} -gt 0 ];
then
	msg 1 "Found ${#FILES[@]} image files."
else
	msg 3 "No image files found. Stop."
fi

${MKDIR} -p "${SUBDIR}" || msg 3 "Error creating subdirectory. Stop."

htmlclear index
htmlheader "${TITLE}" index
htmlprint "<h1>${TITLE}</h1>\\n" index
htmlprint "<div class=\"main\">\\n" index
htmlprint "<table cellspacing=\"5\" cellpadding=\"5\">\\n" index
htmlprint "<tr>\\n" index

# need to precompute filenames because they are referenced back/ahead in the prev/next links
for (( i=0; i < ${#FILES[@]}; i++ )); do FILENAMES[${i}]=$(${BASENAME} "${FILES[${i}]}" | ${SED} s/\ /_/g); done

for (( i=0; i < ${#FILES[@]}; i++ ))
do
	FILE="${FILES[${i}]}"
	FILENAME="${FILENAMES[${i}]}"

	msg 1 "$((i+1))/${#FILES[@]} ${FILE}"

	htmlprint "<td align=\"center\" valign=\"bottom\">" index

	CONVERT_OPTIONS=(-strip -quality 90)

	# look in exif if image needs rotation
	ORIENTATION=$(${JHEAD} "${FILE}" | ${GREP} "Orientation")
	if [ "${ORIENTATION}" ]; then
		X=$(echo "${ORIENTATION}" | ${SED} s/.*rotate\ //)
		CONVERT_OPTIONS+=( -rotate "${X}" )
	fi

	${CONVERT} "${CONVERT_OPTIONS[@]}" -scale "${THUMB_DIM}x${THUMB_DIM} >" "${FILE}" "${SUBDIR}/thumb_${FILENAME}" || msg 3 "Error converting thumbnail."
	NEWDIM_THUMB_X=$(${JHEAD} "${SUBDIR}/thumb_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/\ x.*$// | ${SED} s/^.*:\ //)
	NEWDIM_THUMB_Y=$(${JHEAD} "${SUBDIR}/thumb_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/^.*x\ //)
	msg 0 "scaled thumb image to ${NEWDIM_THUMB_X} x ${NEWDIM_THUMB_Y}"
	${CONVERT} "${CONVERT_OPTIONS[@]}" -scale "${FULL_DIM}x${FULL_DIM} >" "${FILE}" "${SUBDIR}/pic_${FILENAME}" || msg 3 "Error converting full pic."
	NEWDIM_FULL_X=$(${JHEAD} "${SUBDIR}/pic_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/\ x.*$// | ${SED} s/^.*:\ //)
	NEWDIM_FULL_Y=$(${JHEAD} "${SUBDIR}/pic_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/^.*x\ //)
	msg 0 "scaled full image to ${NEWDIM_FULL_X} x ${NEWDIM_FULL_Y}"

	htmlprint "<a href=\"page_${FILENAME}.html\"><img src=\"thumb_${FILENAME}\" width=\"${NEWDIM_THUMB_X}\" height=\"${NEWDIM_THUMB_Y}\" alt=\"${FILENAME}\"/></a><br/>" index
	htmlprint "${FILENAME}" index


	# generate sub-pages
	htmlclear "page_${FILENAME}"
	htmlheader "${TITLE}" "page_${FILENAME}"
	htmlprint "<h2>${TITLE}</h2>\\n" "page_${FILENAME}"
	htmlprint "<div class=\"main\">\\n" "page_${FILENAME}"

	# navigation
	X="&lt;&mdash; [prev]&nbsp;&nbsp;"
	if [ ${i} -gt 0 ];
	then
		htmlprint "<a href=\"page_${FILENAMES[$((i-1))]}.html\">${X}</a>" "page_${FILENAME}"
	else
		htmlprint "<span class=\"hide\">${X}</span>" "page_${FILENAME}"
	fi
	htmlprint "<a href=\"index.html\">&nbsp;[index]&nbsp;</a>" "page_${FILENAME}"
	X="&nbsp;&nbsp;[next] &mdash;&gt;"
	if [ $((i+1)) -lt ${#FILES[@]} ];
	then
		htmlprint "<a href=\"page_${FILENAMES[$((i+1))]}.html\">${X}</a>\\n" "page_${FILENAME}"
	else
		htmlprint "<span class=\"hide\">${X}</span>\\n" "page_${FILENAME}"
	fi

	# table with image and info
	INFOS_SIZE="$(${DU} -k "${SUBDIR}/pic_${FILENAME}" | ${GREP} -o '^[0-9]*')"
	INFOS_DATETIME="$(${JHEAD} "${FILE}" | ${GREP} -e 'Date/Time')"
	INFOS_SETTINGS="$(${JHEAD} "${FILE}" | ${GREP} -e 'Flash' -e 'Focal length' -e 'Exposure time' -e 'Aperture' | ${SED} 's/$/\<br\/\>/g')"
	htmlprint "<table>\\n" "page_${FILENAME}"
	htmlprint "<tr><td colspan=\"2\">\\n" "page_${FILENAME}"
	htmlprint "<img src=\"pic_${FILENAME}\" width=\"${NEWDIM_FULL_X}\" height=\"${NEWDIM_FULL_Y}\" alt=\"${FILENAME}\"/>\\n" "page_${FILENAME}"
	htmlprint "</td></tr>\\n" "page_${FILENAME}"
	htmlprint "<tr>\\n<td align=\"left\">" "page_${FILENAME}"
	htmlprint "<small>File : ${FILENAME} ($((i+1))/${#FILES[@]})<br/>Size : ${INFOS_SIZE} KB<br/>${INFOS_DATETIME}</small>" "page_${FILENAME}"
	htmlprint "</td>\\n<td align=\"left\">" "page_${FILENAME}"
	htmlprint "<small>${INFOS_SETTINGS}</small>" "page_${FILENAME}"
	htmlprint "</td>\\n</tr>\\n</table>\\n" "page_${FILENAME}"
	htmlprint "</div>\\n" "page_${FILENAME}"
	htmlfooter "page_${FILENAME}"

	htmlprint "</td>\\n" index
	if [ $((i%5)) -eq 4 ];
	then
		if [ $((i+1)) -ne ${#FILES[@]} ];
		then
			htmlprint "</tr>\\n<tr>\\n" index
		fi
	fi

done

htmlprint "</tr>\\n" index
htmlprint "</table>\\n" index
htmlprint "</div>\\n" index

htmlfooter index

msg 1 "Finished."