#!/usr/bin/env perl
use warnings;
use strict;
use File::Basename;
use Getopt::Long qw(:config gnu_getopt);
use POSIX;
use Nullroute::Lib;

my $qr_y_m_d = qr/\d{4}-\d{2}-\d{2}/;

my $host;
my $force;
my $force_host;
my $dry_run = 0;
my $do_prefix = 0;
my $recurse = 1;

sub usage {
	print for
	"Usage: $::arg0 [options] <file...>\n",
	"\n",                     #
	"  -f, --force            Rename non-matching files (based on mtime)\n",
	"  -n, --dry-run          Only show what would have been done\n",
	"\n",
	"  -h, --host=STR         Suffix a host name or some other label\n",
	"  --force-host=STR       Likewise but has something to do with --force?\n",
	"  -F, --prefix           Prefix current name (instead of replacing)\n",
	"  --no-recurse           Don't descend into subdirectories\n",
	;
}

sub do_file {
	my ($file) = @_;
	_debug("processing file '$file'");

	my ($old_name, $dir, $old_suffix) = fileparse($file, qr/\.[^.]+$/);
	my $new_name = $old_name;
	my $new_suffix = lc($old_suffix);

	# match known non-date names
	for ($new_name) {
		my @stat = stat($file);
		if (!@stat) {
			_err("could not stat '$file': $!");
			next;
		}
		my @mtime = localtime($stat[9]);

		# arbitrary (host given in command line) -- OneDrive, imgur, etc.
		if ($force || $force_host) {
			my $tail = $force_host ? ".$force_host" : "";
			s/.*/strftime("Screenshot_%Y-%m-%d_%H%M%S", @mtime).$tail/e;
			last;
		}

		# mpv
		if (/^(?:gnome-)?mpv-shot.*$/) {
			s//strftime("Screenshot_%Y-%m-%d_%H%M%S", @mtime)/e;
			last;
		}
		# SnagIt
		if (/^(\w+?)[_-]snag\d+$/) {
			s//strftime("%Y-%m-%d.%H%M%S.", @mtime).$1/e;
			last;
		}
	}

	_debug(" first stage => '$new_name'");

	# normalize to Y-M-D.hms
	for ($new_name) {
		my $tail = "";
		$tail .= ".$host" if $host;
		$tail .= " - $old_name" if $do_prefix;

		# Output of stage 1
		s/^Screenshot_($qr_y_m_d)_(\d{2})(\d{2})(\d{2})\b/$1.$2$3$4$tail/;

		# Windows 10 Snip'n'Sketch
		s/^Annotation ($qr_y_m_d) (\d{2})(\d{2})(\d{2})\b/$1.$2$3$4$tail/;
		# Windows 10 Snipping Tool
		# "Screenshot 2022-02-10 181213.png"
		s/^Screenshot ($qr_y_m_d) (\d{2})(\d{2})(\d{2})\b/$1.$2$3$4$tail/;

		# XnView
		# "capture_2016-06-18_171100.png"
		s/^capture_($qr_y_m_d)_(\d{6})\b/$1.$2$tail/;

		# FFXIV
		# "ffxiv_20112019_212142_787.png"
		s/^ffxiv_(\d{2})(\d{2})(\d{4})_(\d{2})(\d{2})(\d{2})_(\d+)\b/$3-$2-$1.$4$5$6.$7$tail/;

		# Dropbox on Windows
		# "Screenshot 2018-05-12 21.52.18.png"
		s/^Screenshot ($qr_y_m_d) (\d+)\.(\d+)\.(\d+)\b/$1.$2$3$4$tail/;

		# VLC
		# "Snapshot_2013-08-05-08h05m53s235.jpg"
		# "vlcsnap-2015-02-07-21h30m11s81.jpg"
		s/^(?:Snapshot_|vlcsnap-)($qr_y_m_d)-(\d{2})h(\d{2})m(\d{2})s\d+/$1.$2$3$4$tail/;

		# GNOME Shell
		# "Screenshot From 2025-01-21 15-47-28.png"
		# "Screenshot from 2018-10-14 14-36-34.png"
		# "Screencast from 2013-09-04 00:49:14.webm"
		s/\bScreenshot [Ff]rom ($qr_y_m_d) (\d+)[:-](\d+)[:-](\d+)\b/$1.$2$3$4$tail/;
		s/\bScreencast from ($qr_y_m_d) (\d+)[:-](\d+)[:-](\d+)\b/$1.$2$3$4$tail/;

		# Android
		# "Screenshot_20150514.172546.png"
		# "Screenshot_20160417-124530.png"
		# Also unknown similar
		# "Screenshot_20130729_000844.jpg"
		s/^Screenshot_(\d{4})(\d{2})(\d{2})[._-](\d{6})\b/$1-$2-$3.$4$tail/;

		# Not sure
		# "Screenshot_2016-09-18.221101.jpg"
		s/^Screenshot_($qr_y_m_d)\.(\d{6})\b/$1.$2$tail/;
		# "Screenshot_2015-01-10-01-02-17.jpg"
		s/^Screenshot_($qr_y_m_d)-(\d{2})-(\d{2})-(\d{2})\b/$1.$2$3$4$tail/;

		# VirtualBox
		# "VirtualBox_<VM>_<DD>_<MM>_<YYYY>_<hh>_<mm>_<ss>.png"
		# "VirtualBox_Win98 SE_28_05_2019_17_34_24.png"
		s/^VirtualBox_(.+)_(\d{2})_(\d{2})_(\d{4})_(\d{2})_(\d{2})_(\d{2})\b/$4-$3-$2.$5$6$7 $1$tail/;

		# World of Warcraft
		# "WoWScrnShot_<MM><DD><YYYY>_<hh><mm><ss>"
		# "WoWScrnShot_062519_114341.jpg"
		s/^WoWScrnShot_(\d{2})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})\b/20$3-$1-$2.$4$5$6$tail/;
	}

	_debug(" second stage => '$new_name'");

	if ($new_name eq $old_name) {
		# do nothing if only the suffix changes
		return;
	}

	$old_name .= $old_suffix;
	$new_name .= $new_suffix;

	if ($new_name eq $old_name) {
		return;
	} elsif ($dry_run) {
		_info("would rename '$dir$old_name' to '$new_name'");
	} elsif (-e $dir.$new_name) {
		_err("not renaming '$old_name' to '$new_name': target already exists");
	} else {
		_info("renaming '$dir$old_name' to '$new_name'");
		if (!rename($dir.$old_name, $dir.$new_name)) {
			_err("rename '$old_name' to '$new_name': !! $!");
		}
	}
}

sub do_dir {
	my (@dirs) = reverse @_;
	_debug("processing dirs [@dirs]");

	while (@dirs) {
		my $dir = pop @dirs;
		my @next = ();
		_debug("reading '$dir'");
		if (-e "$dir/.nofix") {
			_debug("found '$dir/.nofix'; skipping directory");
		} elsif (opendir(my $dh, $dir)) {
			for my $name (sort readdir($dh)) {
				my $path = "$dir/$name";

				if ($name eq "." || $name eq "..") {
					next;
				}
				elsif (-d $path) {
					next if !$recurse;
					# queue?
					push @next, $path;
					# recursion?
					#do_dir($path);
				}
				elsif (-f $path) {
					do_file($path);
				}
			}
			closedir($dh);
		} else {
			_err("cannot open directory '$dir': $!");
		}
		if (@next) {
			push @dirs, reverse @next;
		}
	}
}

GetOptions(
	"help" => sub { usage(); exit },
	"n|dry-run!" => \$dry_run,
	"f|force!" => \$force,
	"force-host=s" => \$force_host,
	"h|host=s" => \$host,
	"F|prefix!" => \$do_prefix,
	"recurse!" => \$recurse,
) or exit(2);

my @args = @ARGV;

if (!@args) {
	@args = (".");
}

for (@args) {
	_debug("processing arg '$_'");
	if (-d) {
		do_dir($_);
	} else {
		do_file($_);
	}
}

Nullroute::Lib::_exit();
