#!/usr/bin/env bash

# This script takes all the jpg and mp4 files in a directory and
# organises them into a folder structure based on the month and year
# they were last modified (taken).
# 
# Licence: Mozilla Public License 2.0
# Full text: https://www.mozilla.org/en-US/MPL/2.0/
# Summary: https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2)

# Sources and References
#  - Bash controlled paralellisation - http://unix.stackexchange.com/a/216475/64687


# Checks to make sure a specified command is present.
# Adapted from the lantern build engine: https://gitlab.com/sbrl/lantern-build-engine
# $1 - Command name to check for
check_command() {
	echo -n "Checking for $1 - ";
	which $1 >/dev/null 2>&1; exit_code=$?
	if [[ "${exit_code}" -ne 0 ]]; then
		echo "failed! Couldn't locate $1. Make sure it's installed and in your path.";
		exit 2;
	fi
	
	echo "success";
}

# From https://github.com/dylanaraps/pure-bash-bible#use-regex-on-a-string
regex() {
    # Usage: regex "string" "regex"
    [[ $1 =~ $2 ]] && printf '%s\n' "${BASH_REMATCH[1]}"
}

check_command grep;
check_command jhead;
check_command jpegoptim;
check_command optipng;
check_command find;
check_command xargs;
check_command mogrify;
check_command mkdir;
check_command mv;

if [[ -z "${DRY_RUN}" ]]; then
	export DRY_RUN=false;
fi

# Set the number of paralell jobs to the number of cpus
N=$(grep -c ^processor /proc/cpuinfo);

while test "$#" -gt 0
do
	case "$1" in
		--help)
			echo organise-photos
			echo "    by Starbeamrainbowlabs <feedback@starbeamrainbowlabs.com>"
			echo "This script takes all the jpg and mp4 files in a directory and organises them into a folder structure based on the month and year they were last modified (taken)."
			echo 
			echo "Options:"
			echo "    --help"
			echo "         Show this help message"
			echo "    --dry-run"
			echo "         Do a dry run - don't actually move any files."
			exit 
			;;
		--dry-run)
			echo Activating dry run mode.
			export DRY_RUN=true;
	esac
	shift
done

Months=(Zero January February March April May June July August September October November December);

handle_file() {
	local filename=$1;
	
	echo -ne "Processing ${filename} - ";
	
	rawFilename=$(basename "${filename}");
	extension=${filename##*.};
	extension=${extension,,};
	
	takenYear="";
	takenMonth="";
	takenMonthText="";
	
	
	
	
	# Extract the date taken from jpegs
	if [[ "${extension}" == "jpg" ]] || [[ "${extension}" == "jpeg" ]];
	then
		header_info="$(jhead "${filename}")";
		takenDate=$(echo "${header_info}" | grep -i 'Date/Time' | cut -d' ' -f6-7);
		# echo "[DEBUG] datetime: ${takenDate}";
		takenChars=$(echo "${takenDate}" | wc -c);
		if [[ "${takenChars}" -gt 1 ]];
		then
			takenYear=$(echo "${takenDate}" | cut -d ':' -f1);
			takenMonth="$(echo "${takenDate}" | cut -d ':' -f2)";
			takenMonthUnpadded=$(echo ${takenMonth//0});
			takenMonthText=${Months[$takenMonthUnpadded]};
			echo -n "[X] ";
		fi
		
		# echo "year: ${takenYear}, month: ${takenMonth}, month unpadded: ${takenMonthUnpadded}";
	fi
	
	# If it's empty, then we didn't manage to extract from exif
	if [[ -z "${takenMonth}" ]]; then
		# Try the filename
		filename_date="$(echo "${rawFilename}" | grep -iPoh '(?<![0-9])[0-9]{8}(?![0-9])')";
		if [[ -z "${filename_date}" ]]; then
			# Nothing in the filename either - try the date modified
			takenYear=$(date -r "${filename}" +%Y);
			takenMonth=$(date -r "${filename}" +%-m);
			takenMonthText=$(date -r "${filename}" +%B);
			echo -n "[M] ";
		else
			# Extract from the filename
			takenYear="$(echo "${filename_date}" | cut -c 1-4)";
			takenMonth="$(echo "${filename_date}" | cut -c 5-6)";
			takenMonthUnpadded=$(echo ${takenMonth//0});
			takenMonthText=${Months[$takenMonthUnpadded]};
			echo -n "[F] "
		fi
	fi
	
	if [[ "$(echo -n ${takenMonth} | wc -c)" -lt 2 ]]; then
		takenMonth=$(printf "%02d" "${takenMonth}");
	fi
	
	newFolder="${takenYear}/${takenMonth}-${takenMonthText}/";
	newFilename="${newFolder}/${filename}";
	
	echo -ne "filing in ${newFolder} - ";
	
	if [ "${DRY_RUN}" = true ] ; then
		echo dry run
		return
	fi
		
	mkdir -p "${newFolder}";
	mv "${filename}" "${newFilename}";
	
	echo Done\!;
}

# Automatically rotate the images according to their exif data
echo -ne "*** Automatically rotating pictures [1 / 3] ***";
if [[ "${DRY_RUN}" = false ]]; then
	find . -maxdepth 1 \( -iname "*.jpg" -o -iname "*.jpeg" \) -print0 | xargs --verbose -0 -P4 -I {} mogrify -auto-orient '{}';
fi
echo -e "*** done ***";

echo -e "*** Optimising images [2 / 3] ***";
echo -e " * Optimising jpegs";
if [ "${DRY_RUN}" = false ]; then
	find . -maxdepth 1 -regextype egrep -iregex '^.*\.jpe?g$' -print0 | xargs -0 -n1 -P4 jpegoptim --preserve --all-progressive;
fi
echo -e " * Optimising pngs";
if [ "${DRY_RUN}" = false ] && [[ "$(find . -maxdepth 1 -iname "*.png" | wc -l)" -gt "0" ]]; then
	find . -maxdepth 1 -iname "*.png" -print0 | xargs -0 -P4 -n1 optipng -o7 -preserve;
fi
echo -e "*** done ***";


# No need to wrap this in a dry run if, since we do it on a per-file level

find . -maxdepth 1 \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.gif" -o -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mov" -o -iname "*.png" \) -print0 | while read -r -d '' filename
do
	((i=i%N)); ((i++==0)) && wait;
	handle_file "${filename}"
done

echo "*** Complete! ***"

exit 0