unit module HtmlHighlight;

use Rainbow;
use Rainbow::Token;

my $html-template = q:to/EOT/;
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>[[TITLE]]</title>
      <style>
        body {
          background-color: #212426;
          color: white;
          white-space: pre;
          font-family: monospace;
        }
        .t {
          border-right-style: solid;
          border-right-width: 1px;
          border-right-color: #777777;
        }
        [[TOKEN_STYLES]]
      </style>
    </head>
    <body>[[BODY]]
    </body>
    </html>
    EOT
my $span-template = q:to/EOT/;
    <span class="t [[CLASS]]">[[TEXT]]</span>
    EOT

sub ms($color, :$bold, :$italic) {
    "color: $color;"
    ~ ($bold ?? " font-weight: bold;" !! "")
    ~ ($italic ?? " font-style: italic;" !! "")
    ~ ""
}
my %mapping =
    TEXT             => ms("#ffffff"),
    NAME_SCALAR      => ms("#eed891"),
    NAME_ARRAY       => ms("#eed891"),
    NAME_HASH        => ms("#eed891"),
    NAME_CODE        => ms("#eed891"),
    NAME             => ms("#eed891"),
    KEYWORD          => ms("#e04c86", :bold),
    OPERATOR         => ms("#e04c86"),
    TYPE             => ms("#00ffff"),
    ROUTINE          => ms("#978deb"),
    STRING           => ms("#68f3ca"),
    STRING_DELIMITER => ms("#316b5a"),
    ESCAPE           => ms("#fff982"),
    COMMENT          => ms("#6c7292"),
    REGEX_DELIMITER  => ms("#7ffc03"),
    REGEX_LITERAL    => ms("#62c400"),
    REGEX_SPECIAL    => ms("#ffff00"),
    RAKUDOC_TEXT     => ms("#ed63ff"),
    RAKUDOC_MARKUP   => ms("#f8c2ff"),
;
# If you run out of colors, make new ones
# using e.g. https://mokole.com/palette.html
my @more-styles =
    ms("#f8c2ff"),
    ms("#ff1493"),
    ms("#dda0dd"),
    ms("#fa8072"),
    ms("#1e90ff"),
    ms("#ff00ff"),
    ms("#0000ff"),
    ms("#00ffff"),
    ms("#00fa9a"),
    ms("#ba55d3"),
    ms("#00ff00"),
    ms("#ffd700"),
    ms("#ff8c00"),
    ms("#d2b48c"),
    ms("#9acd32"),
;

my $style-text = Rainbow::TokenType.enums.sort(*.value).map(*.key).map(-> $name {
    "." ~ $name ~ " \{\n"
    ~ (%mapping{$name} // @more-styles.pop)
    ~ "\n}\n"
}).join("\n");

our proto sub make-html(|) { * }
multi sub make-html(@tokens, $title --> Str) {
    my @fragments = @tokens.map: -> $t {
        my $t-html = $t.text.subst("&", "&amp;", :g)
                            .subst("<", "&lt;", :g)
                            .subst(">", "&gt;", :g)
        ;
        $span-template.trim()
                      .subst('[[CLASS]]', $t.type.key)
                      .subst('[[TEXT]]', $t-html);
    }

    my $html = $html-template.subst('[[TITLE]]', $title)
                        .subst('[[TOKEN_STYLES]]', $style-text)
                        .subst('[[BODY]]', @fragments.join(''));
}

multi sub make-html(Str $source-text, $title --> Str) {
    my @tokens = Rainbow::tokenize($source-text);
    make-html @tokens, $title;
}
