#! /usr/bin/env perl

use v5.26;
use warnings;
use utf8;

# ABSTRACT: Walk a file hierarchy and output color emojis for Finder tags


use Mac::Finder::Tags;

use experimental 'signatures';
use open qw( :encoding(UTF-8) :std );
use Encode qw( decode_utf8 );
use Getopt::Long 2.33 qw( :config posix_default gnu_getopt );
use Pod::Usage qw( pod2usage );
use Path::Tiny qw( path );

# Use Mac::Finder::Tags caching if more than 100 files were found
our $CACHING_LIMIT = 100;


# Parse CLI parameters

my @paths;
my $maxdepth;
my $verbose;
GetOptions(
	'maxdepth=i' => \$maxdepth,
	'verbose=i' => \$verbose,
	'help|man|?' => \(my $pod_help),
) and @paths = (@ARGV) or pod2usage(2);
pod2usage(-exitstatus => 0, -verbose => 2) if $pod_help;
@paths = map { path(decode_utf8 $_) } @paths;

if (my @missing = grep {! $_->exists} @paths) {
	say STDERR "$0: $_: not found" for @missing;
	exit 1;
}


# Walk the file system

my @bases;
my %result;
for my $base (map {$_->absolute} @paths) {
	my $base_name = $base->basename;
	my $base_dir = $base->parent;
	my $base_len = 1 + length "$base_dir";
	my @found;
	
	say STDERR "Reading file list for $base ..." if $verbose;
	
	my $walk;
	$walk = sub ($path, $depth) {
		return if defined $maxdepth && $depth > $maxdepth;
		my $iter = $path->iterator( {
			recurse         => 0,
			follow_symlinks => 0,
		} );
		while ( defined( my $child = $iter->() ) ) {
			next if $child =~ m{/\.};
			push @found, decode_utf8 substr $child, $base_len;
			if (-d $child && ! -l $child && -x $child) {
				$walk->($child, $depth + 1);
			}
		}
	};
	
	push @found, decode_utf8 substr $base, $base_len;
	$walk->($base, 1);
	
	say STDERR scalar(@found), " files found in $base_name" if $verbose;
	@found = sort { fc $a cmp fc $b } @found;
	
	$result{"$base"} = \@found;
	push @bases, $base;
}


# Read tags from file system and print the result

say STDERR "Reading file tags ..." if $verbose;
my $total_files = scalar map { $result{"$_"}->@* } @bases;
my $caching = $total_files > $CACHING_LIMIT;
my $ft = Mac::Finder::Tags->new( caching => $caching );

say STDERR "Caching tags completed; writing catalog ..." if $verbose && $caching;

for my $base (@bases) {
	my $base_dir = $base->parent;
	for my $subpath ( $result{"$base"}->@* ) {
		print $subpath;
		my $fullpath = $base_dir->child($subpath);
		my @tags = $ft->get_tags($fullpath);
		print " " if @tags;
		print $_->emoji || "\N{WHITE QUESTION MARK ORNAMENT}" for @tags;
		#print " \N{NORTH EAST ARROW}\N{VARIATION SELECTOR-16}" if -l $fullpath;
		print "\n";
	}
}


exit 0;

__END__

=encoding UTF-8

=head1 SYNOPSIS

 eg/colored-find.pl [--maxdepth num] path ...

=head1 DESCRIPTION

This example script recursively descends the directory tree for each path
listed, listing each path along with the color of its Finder tags (if any).
It works similarly to the L<find(1)> Unix utility, but doesn't support any
of that utility's options except for C<--maxdepth>.

The output will be encoded in UTF-8 (not normalised).

Note: If you get "Operation not permitted" errors when executing this script,
the cause are probably the security features introduced with Mac OS X 10.14.
To fix this, open the "Security & Privacy" section in the System Preferences
and add Terminal to the apps that are allowed Full Disk Access.

=head1 EXAMPLES
 
 eg/colored-find.pl '/Volumes/Backup HD' > backup_files.txt
 eg/colored-find.pl ~ --maxdepth 1
 eg/colored-find.pl ~/Movies ~/Music ~/Pictures | grep '🔴'

Example of how output might look:

 Applications 🟠
 Desktop
 Documents
 Downloads
 Library 🟣
 Movies 🟢
 Movies/Servant of the People 🔵🟡
 Music 🟢
 Music/Audio 🔴🟠
 Music/Lyrics
 Music/Music Library ⚫️
 Music/Radio 🔴
 Music/Sheet
 Pictures ⚪️🟢
 Public
 Sites 🟠🟠🟡