#!/usr/bin/env perl

#######################################################################
#            _   _  _____ _____   ______                     _        #
#      ╱╲   | ╲ | |╱ ____|_   _| |  ____|                   | |       #
#     ╱  ╲  |  ╲| | (___   | |   | |__   _ __   ___ ___   __| | ___   #
#    ╱ ╱╲ ╲ | . ` |╲___ ╲  | |   |  __| | '_ ╲ ╱ __╱ _ \ / _` |╱ _ ╲  #
#   ╱ ____ ╲| |╲  |____) |_| |_  | |____| | | | (_| (_) | (_| |  __╱  #
#  ╱_╱    ╲_╲_| ╲_|_____╱|_____| |______|_| |_|╲___╲___╱ ╲__,_|╲___|  #
#######################################################################
#                     Written By Richard Kelsch                       #
#                  © Copyright 2025 Richard Kelsch                    #
#                        All Rights Reserved                          #
#######################################################################

use strict;
use utf8;
use charnames();
use constant {
    TRUE  => 1,
    FALSE => 0,
    YES   => 1,
    NO    => 0,
};

use Term::ANSIScreen qw( :cursor :screen );
use Term::ANSIColor;
use Time::HiRes qw( sleep );
use Term::ANSIEncode;
use Getopt::Long;
use List::Util qw(max);

# use Data::Dumper::Simple;$Data::Dumper::Terse=TRUE;$Data::Dumper::Indent=TRUE;$Data::Dumper::Useqq=TRUE;$Data::Dumper::Deparse=TRUE;$Data::Dumper::Quotekeys=TRUE;$Data::Dumper::Trailingcomma=TRUE;$Data::Dumper::Sortkeys=TRUE;$Data::Dumper::Purity=TRUE;$Data::Dumper::Deparse=TRUE;

# Since UTF-8 is the norm, it's enabled for all needed handles
binmode(STDERR, ":encoding(UTF-8)");
binmode(STDOUT, ":encoding(UTF-8)");
binmode(STDIN,  ":encoding(UTF-8)");

our $VERSION = $Term::ANSIEncode::VERSION;    # Pull in the version from Term::ANSIEncode

my $version   = FALSE;
my $help      = FALSE;
my $tokens    = FALSE;
my $rawtokens = FALSE;
my $symbols   = FALSE;
my $unicode   = FALSE;
my $colors    = FALSE;
my $Dump      = FALSE;

GetOptions(
    'version'   => \$version,
    'help'      => \$help,
    'tokens'    => \$tokens,
    'rawtokens' => \$rawtokens,
    'colors'    => \$colors,
    'symbols'   => \$symbols,
    'dump'      => \$Dump,
    'unicode'   => \$unicode,
);
###
my $text = <<'VERSION';
[% CLS %][% YELLOW %]╔═════════════════════════════════════════════════════════════════════════════╗[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]               [% BRIGHT YELLOW %] _   _ [% GREEN %] _____ [% BRIGHT BLUE %]_____  [% BRIGHT WHITE %] ______                     _            [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]          ╱╲   [% BRIGHT YELLOW %]│ ╲ │ │[% GREEN %]╱ ____│[% BRIGHT BLUE %]_   _│ [% BRIGHT WHITE %]│  ____│                   │ │           [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]         ╱  ╲  [% BRIGHT YELLOW %]│  ╲│ │[% GREEN %] (___  [% BRIGHT BLUE %] │ │   [% BRIGHT WHITE %]│ │__   _ __   ___ ___   __│ │ ___       [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]        ╱ ╱╲ ╲ [% BRIGHT YELLOW %]│ . ` │[% GREEN %]╲___ ╲ [% BRIGHT BLUE %] │ │   [% BRIGHT WHITE %]│  __│ │ '_ ╲ ╱ __╱ _ ╲ ╱ _` │╱ _ ╲      [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]       ╱ ____ ╲[% BRIGHT YELLOW %]│ │╲  │[% GREEN %]____) │[% BRIGHT BLUE %]_│ │_  [% BRIGHT WHITE %]│ │____│ │ │ │ (_│ (_) │ (_│ │  __╱      [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %][% RED %]      ╱_╱    ╲_╲[% BRIGHT YELLOW %]_│ ╲_│[% GREEN %]_____╱[% BRIGHT BLUE %]│_____│ [% BRIGHT WHITE %]│______│_│ │_│╲___╲___╱ ╲__,_│╲___│      [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% B_BLACK %]                                                                             [% YELLOW %]║[% B_BLACK %]
[% YELLOW %]╠═════════════════════════════════════════════════════════════════════════════╣[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]                         Written By [% BRIGHT YELLOW %]Richard Kelsch[% RESET %][% B_COLOR 17 %]                           [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]                       Copyright ©[% GREEN %]2025 [% BRIGHT YELLOW %]Richard Kelsch[% RESET %][% B_COLOR 17 %]                        [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]                            All Rights Reserved                              [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]                           Perl Artistic License                             [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]                               Version [% GREEN %]XXXX[% RESET %][% B_COLOR 17 %]                                  [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]║[% RESET %][% B_COLOR 17 %]              GitHub:  https://github.com/richcsst/ansi-encode               [% RESET %][% YELLOW %]║[% RESET %]
[% YELLOW %]╚═════════════════════════════════════════════════════════════════════════════╝[% RESET %]
VERSION
###
$text =~ s/XXXX/$VERSION/gs;

my $ansi = Term::ANSIEncode->new('mode' => 'full');
# warn Dumper($ansi);exit;
###
my $small = <<'SMALL';
[% CLS %]╔═════════════════════════════════════════════════════════════════════════╗
║[% B_BLACK %]                     [% RED %]┏━┓[% BRIGHT YELLOW %]┏┓╻[% GREEN %]┏━┓[% BRIGHT BLUE %]╻   [% BRIGHT WHITE %]┏━╸┏┓╻┏━╸┏━┓╺┳┓┏━╸                     [% RESET %]║
║[% B_BLACK %]                     [% RED %]┣━┫[% BRIGHT YELLOW %]┃┗┫[% GREEN %]┗━┓[% BRIGHT BLUE %]┃   [% BRIGHT WHITE %]┣╸ ┃┗┫┃  ┃ ┃ ┃┃┣╸                      [% RESET %]║
║[% B_BLACK %]                     [% RED %]╹ ╹[% BRIGHT YELLOW %]╹ ╹[% GREEN %]┗━┛[% BRIGHT BLUE %]╹   [% BRIGHT WHITE %]┗━╸╹ ╹┗━╸┗━┛╺┻┛┗━╸                     [% RESET %]║
╠═════════════════════════════════════════════════════════════════════════╣
║[% B_COLOR 52 %][% BRIGHT YELLOW %] DESCRIPTION                                                             [% RESET %]║
║     Markup text to ANSI encoder.                                        ║
╟─────────────────────────────────────────────────────────────────────────╢
║[% B_COLOR 52 %][% BRIGHT YELLOW %] USAGE                                                                   [% RESET %]║
║     [% CYAN %]ansi-encode[% RESET %] [options] [text file]                                   ║
╟─────────────────────────────────────────────────────────────────────────╢
║[% B_COLOR 52 %][% BRIGHT YELLOW %] OPTIONS                                                                 [% RESET %]║
║     -[% PINK %]v[% RESET %] or --[% PINK %]version[% RESET %]                                                     ║
║         Shows version and licensing info                                ║
║                                                                         ║
║     -[% PINK %]h[% RESET %] or --[% PINK %]help[% RESET %]                                                        ║
║         Usage information                                               ║
║                                                                         ║
║     -[% PINK %]t[% RESET %] or --[% PINK %]tokens[% RESET %]                                                      ║
║         Show most used tokens                                           ║
║                                                                         ║
║     -[% PINK %]r[% RESET %] or --[% PINK %]rawtokens[% RESET %]                                                   ║
║         Raw dump of available tokens.                                   ║
║                                                                         ║
║     -[% PINK %]c[% RESET %] or --[% PINK %]colors[% RESET %]                                                      ║
║         Show available colors                                           ║
║                                                                         ║
║     -[% PINK %]s[% RESET %] or --[% PINK %]symbols[% RESET %] [search]                                            ║
║         Show available symbols and character tokens by name             ║
║                                                                         ║
║     -[% PINK %]u[% RESET %] or --[% PINK %]unicode[% RESET %] [search]                                            ║
║         Show available symbols and character tokens by unicode          ║
║                                                                         ║
║     -[% PINK %]d[% RESET %] or --[% PINK %]dump[% RESET %] [search]                                               ║
║         Dump available sysmbols                                         ║
╚═════════════════════════════════════════════════════════════════════════╝
SMALL
###
my $greydient = '';    # Yes, the spelling is intentional
foreach my $c (0 .. 23) {
    $greydient .= "[% B_GREY $c %] ";
}
$greydient .= '[% RESET %]';
# [% BRIGHT WHITE %]╞══[% B_BLACK %] [% BOLD %][% MAGENTA %]GENERAL [% RESET %][% BRIGHT WHITE %]════════════════════════════╤══════════════════════════════════════════════╡[% RESET %]
###
my $to = <<'TOKENS';
[% CLS %]Tokens have to be encapsulated inside [ % TOKEN % ] (the TOKEN must be
surrounded by at least one space on each side).  Colors beyond the standard 16*
will require a terminal that supports 256 colors, up to 16 million colors.  It
is also highly recommended that your terminal supports UTF-8 for advanced
character/symbol support.  Some terminals may not support some features.

NOTE:  Use "less -r" to view ANSI in "less"

╭─────────────────────────────────────────────────────────────────────────────────────────╮
TOKENS

$to .= '│' . colored(['bright_red'],'             ::::::::::::   ...      :::  .   .,:::::::::.    :::. .::::::.              ') . "│\n";
$to .= '│' . colored(['bright_magenta'], q{             ;;;;;;;;''''.;;;;;;;.   ;;; .;;,.;;;;''''`;;;;,  `;;;;;;`    `              }) . "│\n";
$to .= '│' . colored(['bright_cyan'], q{                  [[    ,[[     \[[, [[[[[/'   [[cccc   [[[[[. '[['[==/[[[[,             }) . "│\n";
$to .= '│' . colored(['bright_blue'], q{                  $$    $$$,     $$$_$$$$,     $$""""   $$$ "Y$c$$  '''    $             }) . "│\n";
$to .= '│' . colored(['bright_green'], q{                  88,   "888,_ _,88P"888"88o,  888oo,__ 888    Y88 88b    dP             }) . "│\n";
$to .= '│' . colored(['bright_yellow'], q{                  MMM     "YMMMMMP"  MMM "MMP" """"YUMMMMMM     YM  "YMmMY"              }) . "│\n";

{
	foreach my $code ('clear','cursor','attributes','foreground','background','horizontal rules','special') {
		if ($code eq 'clear') {
			$to .= '╞══ ' . colored(['bold','bright_white'],uc($code)) . ' ' . '═' x (29 - (2 + length($code))) . '═╤════════════════════════════════════════════════════════╡' . "\n";
		} else {
			$to .= '╞══ ' . colored(['bold','bright_white'],uc($code)) . ' ' . '═' x (29 - (2 + length($code))) . '═╪════════════════════════════════════════════════════════╡' . "\n";
		}
		if ($code eq 'horizontal rules') {
			$to .= '│  ' . sprintf('%-29s', 'HORIZONTAL RULE color') . ' │ ' . sprintf('%-54s','Horizontal rule the width of the screen') . ' │' . "\n";
		} else {
			my @names = (sort(keys %{$ansi->{'ansi_meta'}->{$code}}));
			while(scalar(@names)) {
				my $name = shift(@names);
				$to .= '│  ' . sprintf('%-29s',$name) . ' │ ' . sprintf('%-54s',$ansi->ansi_description($code,$name)) . ' │' . "\n";
			}
		}
	}
}
  
$to .= '╰────────────────────────────────┴────────────────────────────────────────────────────────╯' . "\n";
###

$to =~ s/\[\% GREYDIENT \%\]/$greydient/g;

{
	my $new;
	foreach my $code (qw(attributes foreground background)) {
		if ($code eq 'background') {
			my $new;
			foreach my $name (sort(keys %{$ansi->{'ansi_meta'}->{$code}})) {
				if ($name =~ /B_BLACK|B_NAVY|B_MIDNIGHT BLUE|B_MEDIUM BLUE|B_INDIGO|B_MAROON|B_DEFAULT|B_DARK BLUE|B_BLUE/) {
					$new = $ansi->{'ansi_meta'}->{$code}->{$name}->{'out'} . " $name " . $ansi->{'ansi_meta'}->{'attributes'}->{'RESET'}->{'out'};
				} else {
					$new = $ansi->{'ansi_meta'}->{'foreground'}->{'BLACK'}->{'out'} . $ansi->{'ansi_meta'}->{$code}->{$name}->{'out'} . " $name " . $ansi->{'ansi_meta'}->{'attributes'}->{'RESET'}->{'out'};
				}
				$to =~ s/  $name  / $new /gs;
			}
		} else {
			foreach my $name (sort(keys %{$ansi->{'ansi_meta'}->{$code}})) {
				next if ($name eq 'HIDE');
				my $new = $ansi->{'ansi_meta'}->{$code}->{$name}->{'out'} . $name . $ansi->{'ansi_meta'}->{'attributes'}->{'RESET'}->{'out'};
				$to =~ s/  $name  /  $new  /gs;
			}
		}
	}
	$new = colored(['bright_green'],'│');
	$to =~ s/│/$new/gs;
	$new = colored(['bright_green'],'─');
	$to =~ s/─/$new/gs;
	$new = colored(['bright_green'],'┴');
	$to =~ s/┴/$new/gs;
	$new = colored(['bright_green'],'╭');
	$to =~ s/╭/$new/gs;
	$new = colored(['bright_green'],'╮');
	$to =~ s/╮/$new/gs;
	$new = colored(['bright_green'],'╰');
	$to =~ s/╰/$new/gs;
	$new = colored(['bright_green'],'╯');
	$to =~ s/╯/$new/gs;
	$new = colored(['bright_green'],'╞');
	$to =~ s/╞/$new/gs;
	$new = colored(['bright_green'],'═');
	$to =~ s/═/$new/gs;
	$new = colored(['bright_green'],'╤');
	$to =~ s/╤/$new/gs;
	$new = colored(['bright_green'],'╡');
	$to =~ s/╡/$new/gs;
	$new = colored(['bright_green'],'╪');
	$to =~ s/╪/$new/gs;
}

if ($version) {
    $| = 1;
    $ansi->ansi_output($text);
} elsif ($help) {
    $| = 1;
    $ansi->ansi_output($small);
} elsif ($tokens) {
    $ansi->ansi_output($to);
} elsif ($rawtokens) {
    print "\n╔", '═' x 131, "╗\n";
    $ansi->ansi_output('║[% B_BLACK %][% BRIGHT YELLOW %]' . ' ' x 46 . 'THE FOLLOWING ARE THE AVAILABLE TOKENS' . ' ' x 47 . "[% RESET %]║\n");
    print '╠', '═' x 32, '╦', '═' x 32, '╦', '═' x 32, '╦', '═' x 32, '╣', "\n";
    printf('║ %-31s║ %-31s║ %-31s║ %-31s║', 'COLOR 0 - COLOR 231', 'B_COLOR 0 - B_COLOR 231', 'GREY 0 - GREY 28', 'B_GREY 0 - B_GREY 28');
    print "\n║";
    my $width = 4;
    my $count = 1;
	foreach my $codes ('clear','cursor','attributes','foreground','background') {
		foreach my $token (sort(keys %{ $ansi->{'ansi_meta'}->{$codes} })) {
			next if ($token =~ /COLOR |GREY /);
			printf(' %-31s', $token);
			$count++;
			if ($count > $width) {
				$count = 1;
				print "║\n║";
			} else {
				print '║';
			}
		} ## end foreach my $token (sort(keys...))
	}
    print "\r╚", '═' x 32, '╩', '═' x 32, '╩', '═' x 32, '╩', '═' x 32, '╝', "\n\n";
} elsif ($Dump) {
    my $temp = "\n\n";
    my @names;
    my $search = (scalar(@ARGV)) ? pop(@ARGV) : undef;
    if (defined($search) && $search ne '') {
		$ansi->ansi_output(charnames::string_vianame($search));
    } else {
        my $start = $ansi->{'start'};
        my $finish = $ansi->{'finish'};
		foreach my $code (0x20 .. $finish) {
            next if (($code > 0x7E && $code <= 0x9F) || ($code > 0xFF && $code < 0x2010) || ($code >= 0x2BFF && $code <= 0x1F2FF) || ($code >= 0x1F8C0 && $code <= 0x1F8FF));
            my $name = charnames::viacode($code);
			$ansi->ansi_output(charnames::string_vianame($name) . ' ');
        }
    }
	print "\n";
} elsif ($unicode) {
    my $search = (scalar(@ARGV)) ? pop(@ARGV) : undef;
    $ansi->ansi_output("\nNOTE:  Not all terminals will support all characters\n" . '[% COLOR 52 %]╭───────╮[% RESET %]' . "\n");
    if (defined($search)) {
        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT CYAN %]Unicode[% COLOR 52 %]│[% RESET %]' . "\n");
        $ansi->ansi_output('[% COLOR 52 %]├───────┼─────[% RESET %]' . "\n");
        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT WHITE %]' . sprintf('%6s',$search) . ' [% COLOR 52 %]│[% RESET %] ' . charnames::string_vianame(charnames::viacode(hex($search))));
        print "\n";
        $ansi->ansi_output('[% COLOR 52 %]╰───────┴─────[% RESET %]' . "\n\n");
    } else {
        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT CYAN %]Unicode[% COLOR 52 %]│[% RESET %][% BRIGHT WHITE %] 0 1 2 3 4 5 6 7 8 9 A B C D E F[% RESET %]' . "\n");
        $ansi->ansi_output('[% COLOR 52 %]├───────┼───────────────────────────────────[% RESET %]' . "\n");
        my $count = 0;
        my $start = $ansi->{'start'};
        my $finish = $ansi->{'finish'};
        foreach my $code (0x20 .. $finish) {
            next if (($code > 0x7E && $code <= 0x9F) || ($code > 0xFF && $code < 0x2010) || ($code >= 0x2BFF && $code <= 0x1F2FF) || ($code >= 0x1F8C0 && $code <= 0x1F8FF));
            my $name = charnames::viacode($code);
            my $char = charnames::string_vianame($name);
            my $hcode = substr(sprintf('0x%X',$code),2);
            if ($hcode ne '') {
                if ($hcode eq '1F300') {
                    print "\n";
                    $count = 0;
                }
                if ($code >= 0x20E0 && $code <= 0x20EF) {
                    unless ($count) {
                        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT WHITE %]  ' . $hcode . ' [% COLOR 52 %]│[% RESET %]   ' . $char);
                    } else {
                        $ansi->ansi_output('  ' . $char);
                    }
                } elsif ($code < 0x2BFF) {
                    unless ($count) {
                        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT WHITE %]  ' . $hcode . ' [% COLOR 52 %]│[% RESET %]  ' . $char);
                    } else {
                        $ansi->ansi_output(' ' . $char);
                    }
                } else {
                    unless ($count) {
                        $ansi->ansi_output('[% COLOR 52 %]│[% BRIGHT WHITE %] ' . $hcode . ' [% COLOR 52 %]│[% RESET %] ' . $char);
                    } else {
                        $ansi->ansi_output(' ' . $char);
                    }
                } ## end else [ if ($name =~ /^U0(20D.|20E.|20F0)/)]
                $count++;
                if ($count > 15) {
                    $count = 0;
                    print "\n";
                }
            } ## end if ($name ne '')
        }
        $ansi->ansi_output('[% COLOR 52 %]╰───────┴───────────────────────────────────[% RESET %]' . "\n\n");
    }
#    $ansi->ansi_output($temp);
} elsif ($symbols) {
    my $start  = $ansi->{'start'};
    my $finish = $ansi->{'finish'};
    my @names;
    my $search = (scalar(@ARGV)) ? pop(@ARGV) : undef;
    my $size = 0;
    if (defined($search) && $search ne '') {
		if ($search =~ /^U\+/) {
			$search = substr($search,2);
			foreach my $code (0x20 .. $finish) {
				next if (($code > 0x7E && $code <= 0x9F) || ($code > 0xFF && $code < 0x2010) || ($code >= 0x2BFF && $code <= 0x1F2FF) || ($code >= 0x1F8C0 && $code <= 0x1F8FF));
				my $ucode = sprintf('%X',$code);
				warn $ucode . ' ' . $search;
				if ($ucode =~ /^$search/i) {
					my $name = charnames::viacode($code);
					push(@names, $name);
					$size = max($size, length($name));
				}
			}
		} else {
			foreach my $code (0x20 .. $finish) {
				next if (($code > 0x7E && $code <= 0x9F) || ($code > 0xFF && $code < 0x2010) || ($code >= 0x2BFF && $code <= 0x1F2FF) || ($code >= 0x1F8C0 && $code <= 0x1F8FF));
				my $name = charnames::viacode($code);
				if ($name =~ /$search/i) {
					push(@names, $name);
					$size = max($size, length($name));
				}
			}
		}
    } else {
        foreach my $code (0x20 .. $finish) {
            next if (($code > 0x7E && $code <= 0x9F) || ($code > 0xFF && $code < 0x2010) || ($code >= 0x2BFF && $code <= 0x1F2FF) || ($code >= 0x1F8C0 && $code <= 0x1F8FF));
            my $name = charnames::viacode($code);
            push(@names, $name);
            $size = max($size, length($name));
        }
    }
    $ansi->ansi_output("\nNOTE:  Not all terminals will support all characters\n" . '[% COLOR 52 %]╭─────────┬─' . '─' x $size . '─╮[% RESET %]' . "\n");
    $ansi->ansi_output('[% COLOR 52 %]│[% B_BLACK %][% CYAN %] Unicode [% RESET %][% COLOR 52 %]│[% B_BLACK %]' . ' ' x ($size - 20) . '[% BRIGHT YELLOW %]Character Token Names [% RESET %]' . "[% COLOR 52 %]│[% RESET %]\n");
    $ansi->ansi_output('[% COLOR 52 %]├─────────┼─' . '─' x $size . "─┤[% RESET %]\n");
    while (scalar(@names)) {
        my $name = shift(@names);
        if ($name ne '') {
            if ($name =~ /^COMBINING/) {
                $ansi->ansi_output(sprintf('%s│ %s%-5X %s│%s %' . $size . 's %s│%s   %s', '[% COLOR 52 %]', '[% RESET %]U+', charnames::vianame($name), '[% COLOR 52 %]', '[% RESET %]', $name, '[% COLOR 52 %]', '[% RESET %]', charnames::string_vianame($name)) . "\n");
            } else {
                $ansi->ansi_output(sprintf('%s│ %s%-5X %s│%s %' . $size . 's %s│%s %s', '[% COLOR 52 %]', '[% RESET %]U+', charnames::vianame($name), '[% COLOR 52 %]', '[% RESET %]', $name, '[% COLOR 52 %]', '[% RESET %]', charnames::string_vianame($name)) . "\n");
            }
        } ## end if ($name ne '')
    } ## end while (scalar(@names))
    $ansi->ansi_output('[% COLOR 52 %]╰─────────┴─' . '─' x $size . '─╯[% RESET %]' . "\n\n");
} elsif ($colors) {
    my $grey   = '[% GREY 8 %]';
    my $off    = '[% RESET %]';
    my $string = '';
    $string .= "\n" . '[% BRIGHT WHITE %] ANSI Colors and GREY colors (requires a terminal with 256 color support for all colors)' . $off . "\n";
    $string .= $grey . '╭─────┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──╮' . $off . "\n";
    $string .= $grey . '│' . $off . 'COLOR';
    foreach my $i (0 .. 35) {
        $string .= $grey . '│' . $off . sprintf('%02d', $i);
    }
    $string .= $grey . "│\n├─────┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤$off\n";
    $string .= sprintf('%s│%s %4d%s│%s', $grey, $off, 0, $grey, $off);
    foreach my $i (0 .. 35) {
        if ($i <= 15) {
            $string .= chr(27) . '[48;5;' . $i . 'm  ' . $off . $grey . '│' . $off;
        } else {
            $string .= '  ' . $grey . '│' . $off;
        }
    } ## end foreach my $i (0 .. 35)
    foreach my $i (0 .. 6) {
        my $_i = ($i * 36) + 16;
        $string .= "\n" . $grey . '├─────┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤' . $off . "\n";
        if ($i == 6) {
            $string .= $grey . '│' . $off . 'GREY ' . $grey . '│' . $off;
        } else {
            $string .= sprintf("%s│%s %4d%s│%s", $grey, $off, $_i, $grey, $off);
        }
        foreach my $j (0 .. 35) {
            $string .= chr(27) . '[48;5;' . ($_i + $j) . 'm  ' . chr(27) . '[m' . $grey . '│' . $off;
        }
    } ## end foreach my $i (0 .. 6)
    $string .= "\n" . $grey . '╰─────┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──╯' . $off . "\n\n";
    $string .= 'RGB and B_RGB take three values (0 - 255) for [% RED %]RED[% RESET %],[% GREEN %]GREEN[% RESET %],[% BLUE %]BLUE[% RESET %] for full 24 bit color' . "\n";
    $ansi->ansi_output($string);
    print 'For example:  [% RGB 255,255,255 %] WHITE [% RESET %] will show as ';
    $ansi->ansi_output('[% RGB 255,255,255 %] WHITE [% RESET %]' . "\n");
    print '              [% RGB 255,128,192 %] COLOR [% RESET %] will show as ';
    $ansi->ansi_output('[% RGB 255,128,192 %] COLOR [% RESET %]' . "\n");
} else {    # Output file to STDOUT
    my $file = $ARGV[0];
    if (defined($file) && -e $file) {
        my $size = -s $file;
        open(my $FILE, '<', $file);
        binmode($FILE, ":encoding(UTF-8)");
        read($FILE, my $text, $size);
        close($FILE);
        $ansi->ansi_output($text);
    } else {
        $ansi->ansi_output($small);
        $| = 1;
    }
} ## end else [ if ($version) ]

exit(0);

__END__

=head1 NAME

ANSI Encode

=head1 SYNOPSIS

A markup language to generate basic ANSI text.  A terminal that supports UTF-8 is highly recommended for graphics characters.

=head1 USAGE

 ansi_encode.pl [options] [file or search]

=head1 OPTIONS

Using no options expects a file name.

=over 4

=item --B<version> or -B<v>

Shows name, version information and brief licensing information.

=item --B<help> or -B<h>

Simple usage and options documentation

=item --B<tokens> or -B<y>

Shows the most used tokens available.  A token is encapsulated within [% and %] (with at lease one space on each side)

=item --B<rawtokens> or B<r>

Raw dump of useable tokens.

=item --B<symbols> or -B<s> [search]

Similar to "tokens", but instead shows special symbol character token names.

You may also add a search string to shorten the list.

IT IS HIGHLY SUGGESTED YOU USE A SEARCH STRING.  There are a lot of Unicode characters.  Each character has its own token.

=item --B<unicode> or -B<u> [search]

Similar to "tokens", but instead shows special symbol characters by unicode.

You may also add a search string to shorten the list.

IT IS HIGHLY SUGGESTED YOU USE A SEARCH STRING.

=item --B<dump> or -B<d> [search]

Does a raw dump of the symbols.

=back

=over 4

[% RED %]This is written in red[% RESET %]

B<RESET> changes output text to normal.

=back

=head1 TOKENS

=head2 GENERAL

=over 4

=item B<[% RETURN %]>

ASCII RETURN (13)

=item B<[% LINEFEED %]>

ASCII LINEFEED (10)

=item B<[% NEWLINE %]>

RETURN + LINEFEED (13 + 10)

=item B<[% CLS %]>

Places cursor at top left, screen cleared

=item B<[% CLEAR %]>

Clear screen only, cursor remains where it was

=item B<[% CLEAR LINE %]>

Clear to the end of line from current cursor position

=item B<[% CLEAR DOWN %]>

Clear down from current cursor position

=item B<[% CLEAR UP %]>

Clear up from current cursor position

=item B<[% RESET %]>

Reset all colors and attributes to their defaults

=back

=head2 CURSOR

=over 4

=item B<[% HOME %]>

Moves the cursor to the location 1,1 (Top left of screen)

=item B<[% UP %]> (Non-destructive)

Moves cursor up one step

=item B<[% DOWN %]> (Non-destructive)

Moves cursor down one step

=item B<[% RIGHT %]> (Non-destructive)

Moves cursor right one step

=item B<[% LEFT %]> (Non-destructive)

Moves cursor left one step

=item B<[% SAVE %]>

Save current cursor position (overwrites previous save, only one at a time)

=item B<[% RESTORE %]>

Place cursor at saved position

=item B<[% BOLD %]>

Bold text (not all terminals support this)

=item B<[% FAINT %]>

Faded text (not all terminals support this)

=item B<[% ITALIC %]>

Italicized text (not all terminals support this)

=item B<[% UNDERLINE %]>

Underlined text

=item B<[% SLOW BLINK %]>

Slow cursor blink

=item B<[% RAPID BLINK %]>

Rapid cursor blink (Some terminals just do a slow blink)

=item B<[% LOCATE column,row %]>

Set cursor position

=back

=head2 ATTRIBUTES

=over 4

=item B<[% INVERT %]>

Invert text (flip background and foreground attributes)

=item B<[% REVERSE %]>

Invert text (flip background and foreground attributes)

=item B<[% CROSSED OUT %]>

Crossed out.  Text with a line through the middle.

=item B<{% DEFAULT FONT %]>

Default font.  If the font was changed, then restore to the default font.

=back

=head2 FRAMES

=over 4

=item B<[% BOX color,x,y,width,height,type %]> text here B<[% ENDBOX %]>

Draw a frame around text.  There are many types to choose from.

=back

=head2 COLORS

=over 4

=item B<[% NORMAL %]>

Sets colors to default

=back

=head2 FOREGROUND

There are many more foreground colors available than the sixteen below.  However, the ones below should work on any color terminal.  Other colors may requite 256 and 16 million color support.  Most Linux X-Windows and Wayland terminal software should support the extra colors.  Some Windows terminal software should have "Term256" features.  You can used the "-t" option for all of the color tokens available or use the "RGB" token for access to 16 million colors.

=over 4

=item BLACK          = Black
=item RED            = Red
=item GREEN          = Green
=item YELLOW         = Yellow
=item BLUE           = Blue
=item MAGENTA        = Magenta
=item CYAN           = Cyan
=item WHITE          = White
=item DEFAULT        = Default foreground color
=item BRIGHT BLACK   = Bright black (dim grey)
=item BRIGHT RED     = Bright red
=item BRIGHT GREEN   = Lime
=item BRIGHT YELLOW  = Bright Yellow
=item BRIGHT BLUE    = Bright blue
=item BRIGHT MAGENTA = Bright magenta
=item BRIGHT CYAN    = Bright cyan
=item BRIGHT WHITE   = Bright white

=back

=head2 BACKGROUND

There are many more background colors available than the sixteen below.  However, the ones below should work on any color terminal.  Other colors may requite 256 and 16 million color support.  Most Linux X-Windows and Wayland terminal software should support the extra colors.  Some Windows terminal software should have "Term256" features.  You can used the "-t" option for all of the color tokens available or use the "B_RGB" token for access to 16 million colors.

=over 4

=item B_BLACK          = Black
=item B_RED            = Red
=item B_GREEN          = Green
=item B_YELLOW         = Yellow
=item B_BLUE           = Blue
=item B_MAGENTA        = Magenta
=item B_CYAN           = Cyan
=item B_WHITE          = White
=item B_DEFAULT        = Default background color
=item BRIGHT B_BLACK   = Bright black (grey)
=item BRIGHT B_RED     = Bright red
=item BRIGHT B_GREEN   = Lime
=item BRIGHT B_YELLOW  = Bright yellow
=item BRIGHT B_BLUE    = Bright blue
=item BRIGHT B_MAGENTA = Bright magenta
=item BRIGHT B_CYAN    = Bright cyan
=item BRIGHT B_WHITE   = Bright white

=back

=head2 HORIZONAL RULES

Makes a solid blank line, the full width of the screen with the selected color

For example, for a color of blue, use the following

  [% HORIZONTAL RULE BLUE %]

=over 4

=item HORIZONTAL RULE [color]             = A solid line of [color] background

=back

=head1 AUTHOR & COPYRIGHT

Richard Kelsch

 Copyright (C) 2025 Richard Kelsch
 All Rights Reserved
 Perl Artistic License

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

L<http://www.perlfoundation.org/artistic_license_2_0>

=cut
