#!/usr/bin/env perl

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

use strict;
use utf8; # In Windows, Codepage 65001 needs to be set "chcp 65001 >nul"
use warnings;
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 qw( ansi_colors );
use Getopt::Long;
use List::Util qw(max);
use Text::Format;

# 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;
my $c3bit      = TRUE;
my $c4bit      = TRUE;
my $c8bit      = FALSE;
my $c24bit     = FALSE;
my $frames     = FALSE;
my $rules      = FALSE;
my $ansi_modes = FALSE;
my $width      = 80,
my $baud       = 0;
my $speed      = 0;

GetOptions(
    'a|ansi-modes'       => \$ansi_modes,
    'version'            => \$version,
    'help'               => \$help,
    'tokens'             => \$tokens,
    'rawtokens'          => \$rawtokens,
    'colors'             => \$colors,
    'symbols'            => \$symbols,
    'dump'               => \$Dump,
    'unicode'            => \$unicode,
    'frames'             => \$frames,
    'h|horizontal-rules' => \$rules,
    'baud=i'             => \$baud,
    'width=i'            => \$width,
);

if ($baud > 0) {
    if ($baud < 75) {
        $baud  = 75;
        $speed = 1 / (75 / 8);
    } elsif ($baud < 150) {
        $baud  = 150;
        $speed = 1 / (150 / 8);
    } elsif ($baud < 600) {
        $baud  = 300;
        $speed = 1 / (300 / 8);
    } elsif ($baud < 1200) {
        $baud  = 600;
        $speed = 1 / (600 / 8);
    } elsif ($baud < 2400) {
        $baud  = 1200;
        $speed = 1 / (1200 / 8);
    } elsif ($baud < 4800) {
        $baud  = 2400;
        $speed = 1 / (2400 / 8);
    } elsif ($baud < 9600) {
        $baud  = 4800;
        $speed = 1 / (4800 / 8);
    } elsif ($baud < 19200) {
        $baud  = 9600;
        $speed = 1 / (9600 / 8);
    } elsif ($baud < 38400) {
        $baud  = 19200;
        $speed = 1 / (19200 / 8);
    } elsif ($baud < 57600) {
        $baud  = 38400;
        $speed = 1 / (38400 / 8);
    } elsif ($baud < 115200) {
        $baud  = 57600;
        $speed = 1 / (57600 / 8);
    } else {
        $baud  = 115200;
        $speed = 1 / (115200 / 8);
    }
} else {
    $baud  = 0;
    $speed = 0;
}

if ((exists($ENV{'TERM'}) && $ENV{'TERM'} =~ /256/) || exists($ENV{'Path'})) { # PATH for Linux and Path for Windows
    $c8bit = TRUE;
}
if (exists($ENV{'COLORTERM'}) && $ENV{'COLORTERM'} eq 'truecolor') {
    $c24bit = TRUE;
}

my $header = '[% CLS %]For Best results, make sure your terminal type supports 256 (or more)
colors like "xterm-256color".  You should be using the "Awesome" fonts for
access to all Unicode characters and symbols:

    http://github.com/gabrielelana/awesome-terminal-fonts

I suggest "SourceCodePro-Powerline-Awesome" when selecting a font
';

###

my $ansi = Term::ANSIEncode->new('baud' => $baud, 'speed' => $speed, 'columns' => $width);

our @list = (
    0x20 .. 0x7F,
    0xA0 .. 0xFF,
    0x2010 .. 0x205F,
    0x2070 .. 0x242F,
    0x2440 .. 0x244F,
    0x2460 .. 0x29FF,
    0x1F300 .. 0x1F8BF,
    0x1F900 .. 0x1FBBF,
    0x1FBC0 .. 0x1FBCF,
    0x1FBF0 .. 0x1FBFF,
);

my $bar = '[% BRIGHT GREEN %]│[% RESET %]';

$| = 1;
if ($version) {
###
    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 2.0                           [% 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;
    
    $ansi->ansi_output($text);
} elsif ($ansi_modes) {
    my $out = "\n[% B_YELLOW %][% BLACK %]      ANSI Modes supported       [% RESET %]\n";
    if ($c3bit) {
        $out .= "     ANSI 3 BIT:  [% GREEN %]Supported[% RESET %]\n";
    } else {
        $out .= "     ANSI 3 BIT:  [% RED %]Not Supported[% RESET %]\n";
    }
    if ($c4bit) {
        $out .= "     ANSI 4 BIT:  [% GREEN %]Supported[% RESET %]\n";
    } else {
        $out .= "     ANSI 4 BIT:  [% RED %]Not Supported[% RESET %]\n";
    }
    if ($c8bit) {
        $out .= "     ANSI 8 BIT:  [% GREEN %]Supported[% RESET %]\n";
    } else {
        $out .= "     ANSI 8 BIT:  [% RED %]Not Supported[% RESET %]\n";
    }
    if ($c24bit) {
        $out .= "    ANSI 24 BIT:  [% GREEN %]Supported[% RESET %]\n";
    } else {
        $out .= "    ANSI 24 BIT:  [% RED %]Not Supported[% RESET %]\n";
    }
    $out .= "[% B_YELLOW %]                                 [% RESET %]\n\n";
    $ansi->ansi_output($out);
} elsif ($help) {
    $ansi->ansi_output(help_text());
} elsif ($tokens) {
###
    my $to = <<'TOKENS';
[% CLS %]
[% WRAP %]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.[% ENDWRAP %]

NOTE:  [% ITALIC %]Use "less -r" to view ANSI in "less"[% RESET %]

[% BRIGHT GREEN %]╭─────────────────────────────────────────────────────────────────────────────╮[% RESET %]
TOKENS
###
    $to .= "$bar ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄   ▄    ▄   ▄▄▄▄▄▄▄▄▄▄▄   ▄▄        ▄   ▄▄▄▄▄▄▄▄▄▄▄ $bar\n";
    $to .= "$bar▐[% RED %]░░░░░░░░░░░[% RESET %]▌▐[% RED %]░░░░░░░░░░░[% RESET %]▌ ▐[% RED %]░[% RESET %]▌  ▐[% RED %]░[% RESET %]▌ ▐[% RED %]░░░░░░░░░░░[% RESET %]▌ ▐[% RED %]░░[% RESET %]▌      ▐[% RED %]░[% RESET %]▌ ▐[% RED %]░░░░░░░░░░░[% RESET %]▌$bar\n";
    $to .= "$bar ▀▀▀▀█[% RED %]░[% RESET %]█▀▀▀▀ ▐[% RED %]░[% RESET %]█▀▀▀▀▀▀▀█[% RED %]░[% RESET %]▌ ▐[% RED %]░[% RESET %]▌ ▐[% RED %]░[% RESET %]▌  ▐[% RED %]░[% RESET %]█▀▀▀▀▀▀▀▀▀  ▐[% RED %]░[% RESET %]▌[% RED %]░[% RESET %]▌     ▐[% RED %]░[% RESET %]▌ ▐[% RED %]░[% RESET %]█▀▀▀▀▀▀▀▀▀ $bar\n";
    $to .= "$bar     ▐[% YELLOW %]░[% RESET %]▌     ▐[% YELLOW %]░[% RESET %]▌       ▐[% YELLOW %]░[% RESET %]▌ ▐[% YELLOW %]░[% RESET %]▌▐[% YELLOW %]░[% RESET %]▌   ▐[% YELLOW %]░[% RESET %]▌           ▐[% YELLOW %]░[% RESET %]▌▐[% YELLOW %]░[% RESET %]▌    ▐[% YELLOW %]░[% RESET %]▌ ▐[% YELLOW %]░[% RESET %]▌          $bar\n";
    $to .= "$bar     ▐[% YELLOW %]░[% RESET %]▌     ▐[% YELLOW %]░[% RESET %]▌       ▐[% YELLOW %]░[% RESET %]▌ ▐[% YELLOW %]░[% RESET %]▌[% YELLOW %]░[% RESET %]▌    ▐[% YELLOW %]░[% RESET %]█▄▄▄▄▄▄▄▄▄  ▐[% YELLOW %]░[% RESET %]▌ ▐[% YELLOW %]░[% RESET %]▌   ▐[% YELLOW %]░[% RESET %]▌ ▐[% YELLOW %]░[% RESET %]█▄▄▄▄▄▄▄▄▄ $bar\n";
    $to .= "$bar     ▐[% GREEN %]░[% RESET %]▌     ▐[% GREEN %]░[% RESET %]▌       ▐[% GREEN %]░[% RESET %]▌ ▐[% GREEN %]░░[% RESET %]▌     ▐[% GREEN %]░░░░░░░░░░░[% RESET %]▌ ▐[% GREEN %]░[% RESET %]▌  ▐[% GREEN %]░[% RESET %]▌  ▐[% GREEN %]░[% RESET %]▌ ▐[% GREEN %]░░░░░░░░░░░[% RESET %]▌$bar\n";
    $to .= "$bar     ▐[% GREEN %]░[% RESET %]▌     ▐[% GREEN %]░[% RESET %]▌       ▐[% GREEN %]░[% RESET %]▌ ▐[% GREEN %]░[% RESET %]▌[% GREEN %]░[% RESET %]▌    ▐[% GREEN %]░[% RESET %]█▀▀▀▀▀▀▀▀▀  ▐[% GREEN %]░[% RESET %]▌   ▐[% GREEN %]░[% RESET %]▌ ▐[% GREEN %]░[% RESET %]▌  ▀▀▀▀▀▀▀▀▀█[% GREEN %]░[% RESET %]▌$bar\n";
    $to .= "$bar     ▐[% CYAN %]░[% RESET %]▌     ▐[% CYAN %]░[% RESET %]▌       ▐[% CYAN %]░[% RESET %]▌ ▐[% CYAN %]░[% RESET %]▌▐[% CYAN %]░[% RESET %]▌   ▐[% CYAN %]░[% RESET %]▌           ▐[% CYAN %]░[% RESET %]▌    ▐[% CYAN %]░[% RESET %]▌▐[% CYAN %]░[% RESET %]▌           ▐[% CYAN %]░[% RESET %]▌$bar\n";
    $to .= "$bar     ▐[% CYAN %]░[% RESET %]▌     ▐[% CYAN %]░[% RESET %]█▄▄▄▄▄▄▄█[% CYAN %]░[% RESET %]▌ ▐[% CYAN %]░[% RESET %]▌ ▐[% CYAN %]░[% RESET %]▌  ▐[% CYAN %]░[% RESET %]█▄▄▄▄▄▄▄▄▄  ▐[% CYAN %]░[% RESET %]▌     ▐[% CYAN %]░[% RESET %]▐[% CYAN %]░[% RESET %]▌  ▄▄▄▄▄▄▄▄▄█[% CYAN %]░[% RESET %]▌$bar\n";
    $to .= "$bar     ▐[% BLUE %]░[% RESET %]▌     ▐[% BLUE %]░░░░░░░░░░░[% RESET %]▌ ▐[% BLUE %]░[% RESET %]▌  ▐[% BLUE %]░[% RESET %]▌ ▐[% BLUE %]░░░░░░░░░░░[% RESET %]▌ ▐[% BLUE %]░[% RESET %]▌      ▐[% BLUE %]░░[% RESET %]▌ ▐[% BLUE %]░░░░░░░░░░░[% RESET %]▌$bar\n";
    $to .= "$bar      ▀       ▀▀▀▀▀▀▀▀▀▀▀   ▀    ▀   ▀▀▀▀▀▀▀▀▀▀▀   ▀        ▀▀   ▀▀▀▀▀▀▀▀▀▀▀ $bar\n";

    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]CLEAR [% RESET %][% BRIGHT GREEN %]' . '═' x 27 . '╤' . '═' x 40 . '╡[% RESET %]' . "\n";
    {
        my @names = (sort(keys %{$ansi->{'ansi_meta'}->{'clear'}}));
        while(scalar(@names)) {
            my $name = shift(@names);
            $to .= add_row($bar, $name, $ansi->ansi_description('clear', $name));
        }
    }

    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]CURSOR [% RESET %][% BRIGHT GREEN %]' . '═' x 26 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";

    {
        my @names = (sort(keys %{$ansi->{'ansi_meta'}->{'cursor'}}));
        while(scalar(@names)) {
            my $name = shift(@names);
            $to .= add_row($bar, $name, $ansi->ansi_description('cursor', $name));
        }
        $to .= add_row($bar, 'LOCATE column,row', 'Sets the cursor location');
        $to .= add_row($bar, 'SCROLL UP count',   'Scrolls the screen up by "count" lines');
        $to .= add_row($bar, 'SCROLL DOWN count', 'Scrolls the screen down by "count" lines');
    }

    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]ATTRIBUTES [% RESET %][% BRIGHT GREEN %]' . '═' x 22 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";

    {
        my @names = grep(!/FONT \d/,(sort(keys %{$ansi->{'ansi_meta'}->{'attributes'}})));
        while(scalar(@names)) {
            my $name = shift(@names);
            my $format  = Text::Format->new(
                'columns'     => 37,
                'tabstop'     => 4,
                'extraSpace'  => TRUE,
                'firstIndent' => 0,
            );
            my $desc = $format->format($ansi->ansi_description('attributes',$name));
            my @d = split(/\n/, $desc);
            my $first = TRUE;
            while(scalar(@d)) {
                my $line = shift(@d);
                if ($first) {
                    if ($name =~ /FONT|HIDE|RING BELL|UNDERLINE COLOR/) {
                        $to .= "$bar " . sprintf('%-34s',$name) . ' [% BRIGHT GREEN %]│[% RESET %] ' . sprintf('%-38s',$line) . ' [% BRIGHT GREEN %]│[% RESET %]' . "\n";
#                        $to .= "$bar " . sprintf('%-34s','FONT 1-9') . " $bar " . sprintf('%-38s','Set specific font (1-9)') . " $bar" . "\n" if ($name =~ /FONT DEFAULT/);
                    } else {
                        $to .= $bar . '[% ' . $name . ' %]' . sprintf(' %-34s',$name) . ' [% RESET %]' . "$bar " . sprintf('%-38s',$line) . " $bar\n";
                    }
                    $first = FALSE;
                } else {
                    $to .= sprintf('%s %-34s %s %-38s %s',$bar, ' ', $bar, $line, $bar) . "\n";
                }
            }
        }
        $to .= "$bar " . sprintf('%-34s','UNDERLINE COLOR color') . "$bar " . sprintf('%-38s','Set the underline color using color') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s','token.') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s ','WRAP') . "$bar " . sprintf('%-38s','Begin text block to be word-wrapped') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s ','ENDWRAP') . "$bar " . sprintf('%-38s','End text block to be word-wrapped') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s ','JUSTIFIED') . "$bar " . sprintf('%-38s','Begin text block to be word-wrapped') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s','and justified') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s ','ENDJUSTIFIED') . "$bar " . sprintf('%-38s','End text block to be word-wrapped') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s','and justified') . " $bar\n";
    }

    {
        my $f;
        my $b;
        $to .= '[% BRIGHT GREEN %]╞' . '═' x 36 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";
        $to .= "$bar [% CYAN %]COLORS, START BACKGROUND WITH 'B_' $bar [% CYAN %]DESCRIPTION                            $bar\n";

        foreach my $code ('ANSI 3 BIT','ANSI 4 BIT','ANSI 8 BIT','ANSI 24 BIT') {
            if (($code eq 'ANSI 3 BIT' && $c3bit) || ($code eq 'ANSI 4 BIT' && $c4bit) || ($code eq 'ANSI 8 BIT' && $c8bit) || ($code eq 'ANSI 24 BIT' && $c24bit)) {
                if ($code eq 'ANSI 3 BIT') {
                    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]' . sprintf('%-11s',$code) . ' [% RESET %][% BRIGHT GREEN %]' . '═' x 16 . '╤' . '═' x 4 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";
                } else {
                    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]' . sprintf('%-11s',$code) . ' [% RESET %][% BRIGHT GREEN %]' . '═' x 16 . '╪' . '═' x 4 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";
                }
                my @names = grep(!/^(DEFAULT|COLOR|GRAY)/,(sort(keys %{$ansi->{'ansi_meta'}->{'foreground'}})));
                unshift(@names,'DEFAULT');
                if ($code eq 'ANSI 8 BIT') {
                    foreach my $count (16 .. 231) {
                        push(@names,"COLOR $count");
                    }
                    foreach my $count (0 .. 23) {
                        push(@names,"GRAY $count");
                    }
                }
                foreach my $name (@names) {
                    if (type($ansi->{'ansi_meta'}->{'foreground'}->{$name}->{'out'}) eq $code) {
                        if ($name =~ /^(DEFAULT|NAVY|COLOR 16|COLOR 17|BLACK|MEDIUM BLUE|ARMY GREEN|BISTRE|BULGARIAN ROSE|CHARCOAL|COOL BLACK|DARK BLUE|DARK GREEN|DARK JUNGLE GREEN|DARK MIDNIGHT BLUE|DUKE BLUE|EGYPTIAN BLUE|MEDIUM JUNGLE GREEN|MIDNIGHT BLUE|NAVY BLUE|ONYX|OXFORD BLUE|PHTHALO BLUE|PHTHALO GREEN|PRUSSIAN BLUE|SAINT PATRICK BLUE|SEAL BROWN|SMOKEY BLACK|ULTRAMARINE|ZINNWALDITE BROWN)$/) {
                            $to .= $bar . sprintf(' %-29s ',$name) . '[% RESET %]' . $bar . '[% B_' . $name . ' %]    [% RESET %]│' . sprintf(' %-38s ',$ansi->ansi_description('foreground',$name)) . "$bar\n";
                        } else {
                            $to .= $bar . '[% ' . $name . ' %]' . sprintf(' %-29s ',$name) . '[% RESET %]' . $bar . '[% B_' . $name . ' %]    [% RESET %]' . $bar . sprintf(' %-38s ',$ansi->ansi_description('foreground',$name)) . "$bar\n";
                        }
                    }
                }
            }
        }
        if ($c24bit) {
            $to .= $bar . sprintf(' %-29s ','RGB red,green,blue') . '[% RESET %]' . $bar . '[% B_RED %] [% B_GREEN %] [% B_BLUE %] [% B_MAGENTA %] [% RESET %]' . $bar . sprintf(' %-38s ','Set color to a value 0-255 per primary') . "$bar\n";
            $to .= $bar . sprintf(' %-29s ',' ') . '[% RESET %]' . $bar . '    ' . $bar . sprintf(' %-38s ','color.') . "$bar\n";
        }
    }

    $to .= '[% BRIGHT GREEN %]╞══ [% BOLD %][% BRIGHT YELLOW %]SPECIAL [% RESET %][% BRIGHT GREEN %]' . '═' x 20 . '╧' . '═' x 4 . '╪' . '═' x 40 . '╡[% RESET %]' . "\n";

    {
        my @names = (sort(keys %{$ansi->{'ansi_meta'}->{'special'}}));
        while(scalar(@names)) {
            my $name = shift(@names);
            $to .= '[% BRIGHT GREEN %]│[% RESET %] ' . sprintf('%-34s',$name) . ' [% BRIGHT GREEN %]│[% RESET %] ' . sprintf('%-38s',$ansi->ansi_description('special',$name)) . ' [% BRIGHT GREEN %]│[% RESET %]' . "\n";
        }
        $to .= '[% BRIGHT CYAN %]│ ' . '─' x 34 . " $bar [% BRIGHT CYAN %]" . '─' x 38 . ' │[% RESET %]' . "\n";
        $to .= "$bar " . sprintf('%-34s', 'HORIZONTAL RULE color') . " $bar " . sprintf('%-38s','Horizontal rule the width of the') . " $bar\n";
        $to .= "$bar                                    $bar " . sprintf('%-38s','screen in the specified color.') . ' [% BRIGHT GREEN %]│[% RESET %]' . "\n";
        $to .= '[% BRIGHT CYAN %]│ ' . '─' x 34 . " $bar [% BRIGHT CYAN %]" . '─' x 38 . ' │[% RESET %]' . "\n";
        $to .= "$bar " . sprintf('%-34s','BOX color,col,row,width,hght,type  ') . "$bar " . sprintf('%-38s', 'Shows framed text box in the') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s', 'selected frame type and color.  Text') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s', 'goes between the BOX and ENDBOX token') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s', '') . " $bar\n";
        $to .= "$bar " . sprintf('%34s','types:') . " $bar " . sprintf('%-38s', 'DOUBLE, THIN, THICK, CIRCLE, WEDGE,') . " $bar\n";
        $to .= "$bar " . sprintf('%34s','') . " $bar " . sprintf('%-38s',       'BIG WEDGE, DOTS, DIAMOND, STAR,') . " $bar\n";
        $to .= "$bar " . sprintf('%34s','') . " $bar " . sprintf('%-38s',       'SQUARE, DITHERED, NOTES, BIG ARROWS,') . " $bar\n";
        $to .= "$bar " . sprintf('%34s','') . " $bar " . sprintf('%-38s',       'CHRISTIAN, ARROWS, HEARTS, ROUNDED') . " $bar\n";
        $to .= "$bar " . sprintf('%34s','') . " $bar " . sprintf('%-38s',       'PARALLELOGRAM') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s',' ') . " $bar " . sprintf('%-38s', '') . " $bar\n";
        $to .= "$bar " . sprintf('%-34s','ENDBOX') . " $bar " . sprintf('%-38s', 'Ends the BOX token function') . " $bar\n";
    }
    $to .= '[% BRIGHT GREEN %]╰' . '─' x 36 . '┴' . '─' x 40 . '╯[% RESET %]' . "\n";

    { # Post processing
        my $new = 'UNDERLINE COLOR [% UNDERLINE %][% UNDERLINE COLOR RED %][% FAINT %][% ITALIC %]co[% RESET %][% UNDERLINE %][% UNDERLINE COLOR GREEN %][% FAINT %][% ITALIC %]l[% RESET %][% UNDERLINE %][% UNDERLINE COLOR BLUE %][% FAINT %][% ITALIC %]or[% RESET %]';
        $to =~ s/UNDERLINE COLOR color/$new /gs;

        $new = '[% FAINT %][% ITALIC %] color     [% RESET %]';
        $to =~ s/ color     /$new/gs;

        $new = '[% FAINT %][% ITALIC %] count     [% RESET %]';
        $to =~ s/ count     /$new/gs;

        $new = ' [% RED %][% ITALIC %]red[% RESET %],[% GREEN %][% ITALIC %]green[% RESET %],[% BLUE %][% ITALIC %]blue[% RESET %]';
        $to =~ s/ red,green,blue/$new/gs;

        $new = ' [% FAINT %][% ITALIC %]column[% RESET %],[% FAINT %][% ITALIC %]row[% RESET %] ';
        $to =~ s/ column,row /$new/gs;

        $new = ' [% FAINT %][% ITALIC %]color[% RESET %],[% FAINT %][% ITALIC %]col[% RESET %],[% FAINT %][% ITALIC %]row[% RESET %],[% FAINT %][% ITALIC %]width[% RESET %],[% FAINT %][% ITALIC %]hght[% RESET %],[% FAINT %][% ITALIC %]type[% RESET %]';
        $to =~ s/ color,col,row,width,hght,type/$new/gs;
    }

    $ansi->ansi_output($to);
} elsif ($frames) {
###
    my $sample_frames = <<'FRAMES';

[% BOX BRIGHT YELLOW,1,9,17,5,DOUBLE        %]This is a text box with a DOUBLE frame[% ENDBOX %]
[% BOX BRIGHT GREEN,18,9,20,5,THIN          %]This is a text box with a THIN frame[% ENDBOX %]
[% BOX BRIGHT RED,39,9,16,5,THICK           %]This is a text box with a THICK frame[% ENDBOX %]
[% BOX BRIGHT BLUE,56,9,16,5,CIRCLE         %]This is a text box with a CIRCLE frame[% ENDBOX %]
[% BOX PINK,1,14,20,5,ROUNDED               %]This is a text box with a ROUNDED frame[% ENDBOX %]
[% BOX ORANGE,18,19,16,5,BLOCK              %]This is a text box with a BLOCK frame[% ENDBOX %]
[% BOX BRIGHT BLUE,1,19,16,5,WEDGE          %]This is a text box with a WEDGE frame[% ENDBOX %]
[% BOX MAGENTA,53,18,14,6,DOTS              %]This is a text box with a DOTS frame[% ENDBOX %]
[% BOX CYAN,22,14,17,5,DIAMOND              %]This is a text box with a DIAMOND frame[% ENDBOX %]
[% BOX WHITE,41,14,22,4,STAR                %]This is a text box with a STAR frame[% ENDBOX %]
[% BOX RED,35,19,17,5,SQUARE                %]This is a text box with a SQUARE frame[% ENDBOX %]
[% BOX BRIGHT WHITE,1,24,17,5,DITHERED      %]This is a text box with a DITHERED frame[% ENDBOX %]
[% BOX BRIGHT MAGENTA,19,24,17,5,HEARTS     %]This is a text box with a HEARTS frame[% ENDBOX %]
[% BOX SADDLE BROWN,37,24,17,5,CHRISTIAN    %]This is a text box with a CHRISTIAN frame[% ENDBOX %]
[% BOX ROYAL BLUE,55,24,17,5,ARROWS         %]This is a text box with an ARROWS frame[% ENDBOX %]
[% BOX FOREST GREEN,1,29,22,5,PARALLELOGRAM %]This is a text box with a PARALLELOGRAM frame[% ENDBOX %]
[% BOX CRIMSON,24,29,17,5,BIG WEDGE         %]This is a text box with a BIG WEDGE frame[% ENDBOX %]
[% BOX SALMON,42,29,17,5,BIG ARROWS         %]This is a text box with a BIG ARROWS frame[% ENDBOX %]
[% BOX YELLOW,60,29,16,5,NOTES              %]This is a text box with a NOTES frame[% ENDBOX %]

FRAMES
###
    $ansi->ansi_output("$header\n$sample_frames");
} elsif ($rules) {
    my $sample_horizontal_rules = "Horizontal rules\n\n" .
      '[% HORIZONTAL RULE PINK       %][% BLACK %][% B_PINK       %] PINK[% RESET       %]' . "\n" .
      '[% HORIZONTAL RULE GREEN      %][% BLACK %][% B_GREEN      %] GREEN[% RESET      %]' . "\n" .
      '[% HORIZONTAL RULE ORANGE     %][% BLACK %][% B_ORANGE     %] ORANGE[% RESET     %]' . "\n" .
      '[% HORIZONTAL RULE MAGENTA    %][% BLACK %][% B_MAGENTA    %] MAGENTA[% RESET    %]' . "\n" .
      '[% HORIZONTAL RULE CYAN       %][% BLACK %][% B_CYAN       %] CYAN[% RESET       %]' . "\n" .
      '[% HORIZONTAL RULE BLUE       %][% WHITE %][% B_BLUE       %] BLUE[% RESET       %]' . "\n" .
      '[% HORIZONTAL RULE RED        %][% WHITE %][% B_RED        %] RED[% RESET        %]' . "\n" .
      '[% HORIZONTAL RULE YELLOW     %][% BLACK %][% B_YELLOW     %] YELLOW[% RESET     %]' . "\n" .
      '[% HORIZONTAL RULE NAVY       %][% WHITE %][% B_NAVY       %] NAVY[% RESET       %]' . "\n" .
      '[% HORIZONTAL RULE ROYAL BLUE %][% WHITE %][% B_ROYAL BLUE %] ROYAL BLUE[% RESET %]' . "\n\n";
    $ansi->ansi_output("$header\n$sample_horizontal_rules\rCan be all colors your terminal supports.\n\n");
} elsif ($rawtokens) {
    my $width = 2;
    my $text = "\n";
    if ($width == 2) {
        $text .= '[% GREEN %]╔' . '═' x 65 . '╗[% RESET %]' . "\n";
        $text .= '[% GREEN %]║[% B_BLACK %][% BRIGHT YELLOW %]' . ' ' x 13 . 'THE FOLLOWING ARE THE AVAILABLE TOKENS' . ' ' x 14 . '[% RESET %][% GREEN %]║[% RESET %]' . "\n";
        $text .= '[% GREEN %]╠' . '═' x 32 . '╦' . '═' x 32 . '╣[% RESET %]' . "\n";
    } else {
        $text .=  '[% GREEN %]╔' . '═' x 131 . '╗[% RESET %]' . "\n";
        $text .= '[% GREEN %]║[% B_BLACK %][% BRIGHT YELLOW %]' . ' ' x 46 . 'THE FOLLOWING ARE THE AVAILABLE TOKENS' . ' ' x 47 . '[% RESET %][% GREEN %]║[% RESET %]' . "\n";
        $text .= '[% GREEN %]╠' . '═' x 32 . '╦' . '═' x 32 . '╦' . '═' x 32 . '╦' . '═' x 32 . '╣[% RESET %]' . "\n";
    }
    $text .= '[% GREEN %]║[% RESET %]';
    my $count = 1;
    foreach my $codes ('clear','cursor','attributes','foreground','background') {
        foreach my $token (sort(keys %{ $ansi->{'ansi_meta'}->{$codes} })) {
            $text .= sprintf(' %-31s', $token);
            $count++;
            if ($count > $width) {
                $count = 1;
                $text .= '[% GREEN %]║[% RESET %]' . "\n" . '[% GREEN %]║[% RESET %]';
            } else {
                $text .= '[% GREEN %]║[% RESET %]';
            }
        }
    }
    if ($width == 2) {
        $text .= "\r" . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s','JUSTIFIED') . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s','ENDJUSTIFIED') . '[% GREEN %]║[% RESET %]' . "\n";
        $text .= '[% GREEN %]║[% RESET %]' . sprintf(' %-31s', 'WRAP') . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s', 'ENDWRAP') . '[% GREEN %]║[% RESET %]' . "\n";
        $text .= '[% GREEN %]╚' . '═' x 32 . '╩' . '═' x 32 . '╝[% RESET %]' . "\n\n";
    } else {
        $text .= "\r" . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s','JUSTIFIED') . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s','ENDJUSTIFIED') . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s', 'WRAP') . '[% GREEN %]║[% RESET %]' . sprintf(' %-31s', 'ENDWRAP') . '[% GREEN %]║[% RESET %]' . "\n";
        $text .= '[% GREEN %]╚' . '═' x 32 . '╩' . '═' x 32 . '╩' . '═' x 32 . '╩' . '═' x 32 . '╝[% RESET %]' . "\n\n";
    }
    $ansi->ansi_output($text);
} elsif ($Dump) {
    my $temp = "\n\n";
    my @names;
    my $search = (scalar(@ARGV)) ? uc(pop(@ARGV)) : undef;
    if (defined($search) && $search ne '') {
        $ansi->ansi_output(charnames::string_vianame($search));
    } else {
        print "\nAssembling character dump...";
        my $start = $ansi->{'start'};
        foreach my $code (@list) {
            my $name = charnames::viacode($code);
            if (defined($name)) {
                $temp .= charnames::string_vianame($name) . ' ';
            }
        }
        $ansi->ansi_output($temp);
    }
    print "\n";
} elsif ($unicode) {
    print "\nAssembling unicode glyphs...";
    my $search = (scalar(@ARGV)) ? uc(pop(@ARGV)) : undef;
    my $text = "\n\nNOTE:  Not all terminals will support all characters\n" . '[% COLOR 52 %]╭─────────╮[% RESET %]' . "\n";
    if (defined($search)) {
        $text .= '[% COLOR 52 %]│ [% BRIGHT CYAN %]Unicode[% COLOR 52 %] │[% RESET %]' . "\n";
        $text .= '[% COLOR 52 %]├─────────┼─────[% RESET %]' . "\n";
        $text .= '[% COLOR 52 %]│[% BRIGHT WHITE %]' . sprintf('U+%05s',$search) . ' [% COLOR 52 %]│[% RESET %] ' . charnames::string_vianame(charnames::viacode(hex($search)));
        $text .= "\n";
        $text .= '[% COLOR 52 %]╰─────────┴─────[% RESET %]' . "\n\n";
    } else {
        $text .= '[% 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";
        $text .= '[% COLOR 52 %]├─────────┼' . '─' x 65 . '[% RESET %]' . "\n";
        my $count = 0;
        foreach my $code (@list) {
            my ($name,$char,$hcode);
            $name = charnames::viacode($code);
            if (defined($name) && $name ne '') {
                $char = charnames::string_vianame($name) || ' ';
                $hcode = sprintf('U+%-5s',substr(sprintf('0x%05X',$code),2));
                if ($hcode ne '') {
                    unless ($count) {
                        $text .= '[% COLOR 52 %]│[% BRIGHT WHITE %] ' . $hcode . ' [% COLOR 52 %]│[% RESET %] ' . $char;
                    } else {
                        $text .= '  ' . $char;
                    }
                    $count++;
                    if ($count > 15) {
                        $count = 0;
                        $text .= "\n";
                        $text .= '[% COLOR 52 %]├─────────┼' . '─' x 65 . '[% RESET %]' . "\n" if ($code < 0x1FBFF);
                    }
                }
            }
        }
        $text .= "\n";
        $text .= '[% COLOR 52 %]╰─────────┴' . '─' x 65 . '[% RESET %]' . "\n\n";
    }
    $ansi->ansi_output($text);
} elsif ($symbols) {
    print "\nAssembling symbols...";
    my @names;
    my $search = (scalar(@ARGV)) ? uc(pop(@ARGV)) : undef;
    my $size = 0;
    if (defined($search) && $search ne '') {
        if ($search =~ /^U\+/) {
            $search = substr($search,2);
            foreach my $code (@list) {
                my $ucode = sprintf('%X',$code);
                if ($ucode =~ /^$search/i) {
                    my $name = charnames::viacode($code);
                    push(@names, $name);
                    $size = max($size, length($name));
                }
            }
        } else {
            foreach my $code (@list) {
                my $name = charnames::viacode($code);
                if (defined($name) && $name =~ /$search/i) {
                    push(@names, $name);
                    $size = max($size, length($name));
                }
            }
        }
    } else {
        foreach my $code (@list) {
            my $name = charnames::viacode($code);
            if (defined($name)) {
                push(@names, $name);
                $size = max($size, length($name));
            }
        }
    }
    if (scalar(@names)) {
        my $text = "\nNOTE:  Not all terminals will support all characters\n" . '[% COLOR 52 %]╭─────────┬─' . '─' x $size . '─╮[% RESET %]' . "\n";
        $text   .= '[% COLOR 52 %]│[% B_BLACK %][% CYAN %] Unicode [% RESET %][% COLOR 52 %]│[% B_BLACK %]' . ' ' x ($size - 20) . '[% BRIGHT YELLOW %]Character Token Names [% RESET %]' . "[% COLOR 52 %]│[% RESET %]\n";
        $text   .= '[% COLOR 52 %]├─────────┼─' . '─' x $size . "─┤[% RESET %]\n";
        while (scalar(@names)) {
            my $name = shift(@names);
            if ($name ne '') {
                if ($name =~ /^COMBINING/) {
                    $text .= 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 {
                    $text .= 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))
        $text .= '[% COLOR 52 %]╰─────────┴─' . '─' x $size . '─╯[% RESET %]' . "\n\n";
        $ansi->ansi_output($text);
    } else {
        print "\nNothing found for $search\n\n";
    }
} elsif ($colors) {
    $ansi->ansi_output($ansi->ansi_colors({'3 BIT' => $c3bit, '4 BIT' => $c4bit, '8 BIT' => $c8bit, '24 BIT' => $c24bit}));
} else {    # Output file to STDOUT
    my $file = $ARGV[0];
    if (defined $file && -e $file) {
        open my $fh, '<:encoding(UTF-8)', $file
          or do { warn "Could not open file '$file': $!\n"; exit 2; };
        # Stream the file to the output renderer to avoid huge memory usage.
        while (my $chunk = <$fh>) {
            $ansi->ansi_output($chunk);
        }
        close $fh;
    } else {
        $ansi->ansi_output(help_text());
        $| = 1;
    }
} ## end else [ if ($version) ]

exit(0);

sub type {
    my $text = substr(shift, 2);

    if ($text =~ /^38;2;\d+;\d+;\d+m/) {
        return ('ANSI 24 BIT');
    } elsif ($text =~ /^38;5;\d+m/) {
        return ('ANSI 8 BIT');
    } elsif ($text =~ /^(\d+)m/) {
        my $color = $1 + 0;
        if (($color >= 30 && $color <= 37) || ($color >= 40 && $color <= 47) || $color == 39 || $color == 49) {
            return('ANSI 3 BIT');
        } elsif (($color >= 90 && $color <= 97) || ($color >= 100 && $color <= 107)) {
            return('ANSI 4 BIT');
        }
    }
} ## end sub ansi_type

sub add_row {
    my ($bar, $name, $desc) = @_;

    my $text = '';
    my $format  = Text::Format->new(
        'columns'     => 37,
        'tabstop'     => 4,
        'extraSpace'  => TRUE,
        'firstIndent' => 0,
    );
    $desc = $format->format($desc);
    my @d = split(/\n/, $desc);
    my $first = TRUE;
    while(scalar(@d)) {
        my $line = shift(@d);
        if ($first) {
            $text .= sprintf('%s %-34s %s %-38s %s',$bar, $name, $bar, $line, $bar) . "\n";
            $first = FALSE;
        } else {
            $text .= sprintf('%s %-34s %s %-38s %s',$bar, ' ', $bar, $line, $bar) . "\n";
        }
    }
    return($text);
}

sub help_text {
    my $help_text = <<'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 %]║
║     -[% BRIGHT BLUE %]a[% RESET %] or --[% BRIGHT BLUE %]ansi-modes[% RESET %]                                          ║
║         Show supported ANSI color modes                         ║
║                                                                 ║
║     --[% BRIGHT BLUE %]baud[% RESET %]=speed                                                ║
║         "speed" can be 75, 150, 300, 600, 1200, 2400, 4800      ║
║                        9600, 19200, 38400 or 115200             ║
║                                                                 ║
║     -[% BRIGHT BLUE %]c[% RESET %] or --[% BRIGHT BLUE %]colors[% RESET %]                                              ║
║         Show available colors                                   ║
║                                                                 ║
║     -[% BRIGHT BLUE %]d[% RESET %] or --[% BRIGHT BLUE %]dump[% RESET %] [search]                                       ║
║         Dump available sysmbols                                 ║
║                                                                 ║
║     -[% BRIGHT BLUE %]f[% RESET %] or --[% BRIGHT BLUE %]frames[% RESET %]                                              ║
║         Show sample frame types                                 ║
║                                                                 ║
║     -[% BRIGHT BLUE %]h[% RESET %] or --[% BRIGHT BLUE %]horizontal-rules[% RESET %]                                    ║
║         Show sample horizontal rules                            ║
║                                                                 ║
║     -[% BRIGHT BLUE %]r[% RESET %] or --[% BRIGHT BLUE %]rawtokens[% RESET %]                                           ║
║         Raw dump of available tokens.                           ║
║                                                                 ║
║     -[% BRIGHT BLUE %]s[% RESET %] or --[% BRIGHT BLUE %]symbols[% RESET %] [search]                                    ║
║         Show available symbols and character tokens by name     ║
║                                                                 ║
║     -[% BRIGHT BLUE %]t[% RESET %] or --[% BRIGHT BLUE %]tokens[% RESET %]                                              ║
║         Show most used tokens                                   ║
║                                                                 ║
║     -[% BRIGHT BLUE %]u[% RESET %] or --[% BRIGHT BLUE %]unicode[% RESET %] [search]                                    ║
║         Show available symbols and character tokens by unicode  ║
║                                                                 ║
║     -[% BRIGHT BLUE %]v[% RESET %] or --[% BRIGHT BLUE %]version[% RESET %]                                             ║
║         Shows version and licensing info                        ║
╚═════════════════════════════════════════════════════════════════╝
SMALL
###
    return($help_text);
}

__END__

=pod

=encoding utf8

=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<https://perlfoundation.org/artistic-license-20.html>

=cut
