#!/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 %][% ORANGE %]╔═════════════════════════════════════════════════════════════════════════════╗[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]               [% BRIGHT YELLOW %] _   _ [% GREEN %] _____ [% BRIGHT BLUE %]_____  [% BRIGHT WHITE %] ______                     _            [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]          ╱╲   [% BRIGHT YELLOW %]│ ╲ │ │[% GREEN %]╱ ____│[% BRIGHT BLUE %]_   _│ [% BRIGHT WHITE %]│  ____│                   │ │           [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]         ╱  ╲  [% BRIGHT YELLOW %]│  ╲│ │[% GREEN %] (___  [% BRIGHT BLUE %] │ │   [% BRIGHT WHITE %]│ │__   _ __   ___ ___   __│ │ ___       [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]        ╱ ╱╲ ╲ [% BRIGHT YELLOW %]│ . ` │[% GREEN %]╲___ ╲ [% BRIGHT BLUE %] │ │   [% BRIGHT WHITE %]│  __│ │ '_ ╲ ╱ __╱ _ ╲ ╱ _` │╱ _ ╲      [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]       ╱ ____ ╲[% BRIGHT YELLOW %]│ │╲  │[% GREEN %]____) │[% BRIGHT BLUE %]_│ │_  [% BRIGHT WHITE %]│ │____│ │ │ │ (_│ (_) │ (_│ │  __╱      [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %][% RED %]      ╱_╱    ╲_╲[% BRIGHT YELLOW %]_│ ╲_│[% GREEN %]_____╱[% BRIGHT BLUE %]│_____│ [% BRIGHT WHITE %]│______│_│ │_│╲___╲___╱ ╲__,_│╲___│      [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% B_BLACK %]                                                                             [% ORANGE %]║[% B_BLACK %]
[% ORANGE %]╠═════════════════════════════════════════════════════════════════════════════╣[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]                         Written By [% BRIGHT YELLOW %]Richard Kelsch[% RESET %][% B_COLOR 17 %]                           [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]                       Copyright ©[% GREEN %]2025 [% BRIGHT YELLOW %]Richard Kelsch[% RESET %][% B_COLOR 17 %]                        [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]                            All Rights Reserved                              [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]                           Perl Artistic License                             [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]                               Version [% GREEN %]XXXX[% RESET %][% B_COLOR 17 %]                                  [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]║[% RESET %][% B_COLOR 17 %]              GitHub:  https://github.com/richcsst/ansi-encode               [% RESET %][% ORANGE %]║[% RESET %]
[% ORANGE %]╚═════════════════════════════════════════════════════════════════════════════╝[% 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 and tokens                                ║
║                                                                         ║
║     -[% 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 8
will require a terminal that supports 256 colors.  It is also 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') {
		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 (screen width)') . ' │' . "\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}})) {
				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

=head1 USAGE

 ansi_encode.pl file

=head1 OPTIONS

=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 tokens names.

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

IT IS HIGHLY SUGGESTED YOU USE A SEARCH STRING.

=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 8

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

B<RESET> changes output text to normal.

=back

=head1 TOKENS

=head2 GENERAL

=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

=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

=head2 CURSOR

=item B<[% HOME %]>

Moves the cursor to the location 1,1.

=item B<[% UP %]>

Moves cursor up one step

=item B<[% DOWN %]>

Moves cursor down one step

=item B<[% RIGHT %]>

Moves cursor right one step

=item B<[% LEFT %]>

Moves cursor left one step

=item B<[% SAVE %]>

Save cursor position

=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

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

Set cursor position

=head2 ATTRIBUTES

=item B<[% INVERT %]>

Invert text (flip background and foreground attributes)

=item B<[% REVERSE %]>

Reverse

=item B<[% CROSSED OUT %]>

Crossed out

=item B<{% DEFAULT FONT %]>

Default font

=head2 FRAMES

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

Draw a frame around text

=head2 COLORS

=item B<[% NORMAL %]>

Sets colors to default

=head2 FOREGROUND

=item BLACK          = Black
=item RED            = Red
=item PINK           = Hot pink
=item ORANGE         = Orange
=item NAVY           = Deep blue
=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

=head2 BACKGROUND

=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 B_PINK           = Hot pink
=item B_ORANGE         = Orange
=item B_NAVY           = Deep blue
=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

=head2 HORIZONAL RULES

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

=item HORIZONTAL RULE RED             = A solid line of red background
=item HORIZONTAL RULE GREEN           = A solid line of green background
=item HORIZONTAL RULE YELLOW          = A solid line of yellow background
=item HORIZONTAL RULE BLUE            = A solid line of blue background
=item HORIZONTAL RULE MAGENTA         = A solid line of magenta background
=item HORIZONTAL RULE CYAN            = A solid line of cyan background
=item HORIZONTAL RULE PINK            = A solid line of hot pink background
=item HORIZONTAL RULE ORANGE          = A solid line of orange background
=item HORIZONTAL RULE WHITE           = A solid line of white background
=item HORIZONTAL RULE BRIGHT RED      = A solid line of bright red background
=item HORIZONTAL RULE BRIGHT GREEN    = A solid line of bright green background
=item HORIZONTAL RULE BRIGHT YELLOW   = A solid line of bright yellow background
=item HORIZONTAL RULE BRIGHT BLUE     = A solid line of bright blue background
=item HORIZONTAL RULE BRIGHT MAGENTA  = A solid line of bright magenta background
=item HORIZONTAL RULE BRIGHT CYAN     = A solid line of bright cyan background
=item HORIZONTAL RULE BRIGHT WHITE    = A solid line of bright white background

=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
