### ----------------------------------------------------
### -- fornax
### -- Licenses: ISC
### -- Authors: Andinus <andinus@nand.sh>
### -- File: lib/Fornax/GenerateFrame.rakumod
### ----------------------------------------------------
use Cairo;
use Fornax::Hex2RGB;

# Cells as defined by fornax format.
constant $PATH = '.';
constant $BLOK = '#';
constant $DEST = '$';
constant $STRT = '^';
constant $VIS = '-';
constant $CUR = '@';
constant $CURPATH = '~';

# Colors.
constant %C = (
    bg-main => "#ffffff",

    red-subtle-bg => "#f2b0a2",
    blue-subtle-bg => "#b5d0ff",
    cyan-subtle-bg => "#c0efff",
    green-subtle-bg => "#aecf90",

    fg-main => "#000000",

    fg-special-cold => "#093060",
    fg-special-warm => "#5d3026",
    fg-special-mild => "#184034",
    fg-special-calm => "#61284f",
).map: {.key => hex2rgb(.value)};

enum IterStatus <Walking Blocked Completed>;

sub generate-frame(
    :%CANVAS, :$out, :%excess, :$side, :%meta, :$iter is copy,
    :$idx, :$debug,
) is export {
    my IterStatus $status;
    given $iter.substr(0, 1) {
        when '|' { $status = Completed }
        when '!' { $status = Blocked }
        default { $status = Walking }
    };

    # Remove marker.
    $iter .= substr(1) if $status == Completed|Blocked;

    put "\n[fornax] $idx $iter $status" if $debug;

    my @grid = $iter.comb.rotor: %meta<cols>;
    warn "Invalid grid: $idx $iter $status" unless @grid.elems == %meta<rows>;

    given Cairo::Image.create(
        Cairo::FORMAT_ARGB32, %CANVAS<width>, %CANVAS<height>
    ) {
        given Cairo::Context.new($_) {
            # Paint the entire canvas white.
            .rgb: |%C<bg-main>;
            .rectangle(0, 0, %CANVAS<width>, %CANVAS<height>);
            .fill;

            # This seems to be slower than creating an intermediate
            # variable and assigning from that. Difference is not much
            # so we'll ignore it.
            for ^%meta<rows> X ^%meta<cols>  -> ($r, $c) {
                my Int @target = %excess<width> div 2 + $c * $side,
                                 %excess<height> div 2 + $r * $side,
                                 $side, $side;

                .rectangle: |@target;

                given @grid[$r][$c] -> $cell {
                    # According to the format, current position may be
                    # prioritized over Destination symbol so we
                    # colorize it according to $status.
                    when $cell eq $CUR {
                        .rgba: |%C<fg-special-cold>, 0.56;
                        .rgba: |%C<fg-special-mild>, 0.72 if $status == Completed;
                        .rgba: |%C<fg-special-warm>, 0.72 if $status == Blocked;
                    }
                    when $cell eq $CURPATH {
                        .rgba: |%C<blue-subtle-bg>, 0.84;
                        .rgba: |%C<green-subtle-bg>, 0.96 if $status == Completed;
                        .rgba: |%C<red-subtle-bg>, 0.96 if $status == Blocked;
                    }
                    when $cell eq $VIS {
                        .rgba: |%C<cyan-subtle-bg>, 0.84;
                    }
                    when $cell eq $BLOK { .rgba: |%C<fg-main>, 0.48 }
                    when $cell eq $STRT|$DEST { .rgba: |%C<fg-special-mild>, 0.72 }
                    default { .rgba: |%C<fg-main>, 0.08 }
                }
                .fill :preserve;

                .rgb: |%C<fg-main>;
                .stroke;
            }
        }
        .write_png($out);
        .finish;
    }
}


