#- bcinfo ---------------------------------------------------------------------
# CLI to provide info about a given bytecode file

use MoarVM::Bytecode;

my %*SUB-MAIN-OPTS = :named-anywhere, :coerce-allomorphs-to(Str);

#- helper subs -----------------------------------------------------------------
sub divider() {
    say '#-------------------------------------------------------------------------------';
}

#- MAIN ------------------------------------------------------------------------
sub MAIN(
  Str   $file,         #= use target, setting letter or bytecode path
  Str  :$filename,     #= select frames with given filename
  Str  :$name,         #= select frames with given name
  Str  :$opcodes,      #= select frames containing opcodes
  Bool :$header,       #= show header information
  Bool :$decomp,       #= de-compile file / selected frames
  Bool :$hexdump,      #= show hexdump of selected frames
  Bool :$verbose,      #= be verbose when possible
) {
    CATCH {
        note .message;
        exit 1;
    }

    my $matcher;
    my $grepper;
    if $opcodes {
        my @opcodes = $opcodes.split(',');
        if @opcodes.elems == 1 {
            $matcher :=              *.contains($opcodes);
            $grepper := *.first(*.name.contains($opcodes));
        }
        else {
            my %lookup is Map = @opcodes.kv.reverse;
            $matcher :=         { %lookup{$_   }:exists }
            $grepper := *.first({ %lookup{.name}:exists });
        }
    }

    my sub show(\seq) {
        my $divider := $hexdump || $decomp;
        for seq {
            divider                           if $divider;
            say .gist(:$verbose);
            say ""                            if $divider;
            say .hexdump(:highlight)   ~ "\n" if $hexdump;

            say ($matcher
              ?? .de-compile($matcher, :$verbose)
              !! .de-compile(          :$verbose)
            ) ~ "\n"                          if $decomp;

            LAST divider                      if $divider;
        }
    }

    my $target = $file;
    if $target.chars > 1 && !$target.contains('/' | '\\') {
        with (try "use Identity::Utils <bytecode-io>; &bytecode-io".EVAL)
          andthen .($target) {
            $target = $_;
        }
    }

    my $M := MoarVM::Bytecode.new($target);
    if $header {
        say $M.gist(:$verbose);
        say "";
    }

    if $filename || $name {
        my $seq := $M.frames;
        show $filename
          ?? $name
            ?? $seq.grep({
                   .filename.contains($filename) && .name.contains($name)
               })
            !! $seq.grep(*.filename.contains($filename))
          !! $seq.grep(*.name.contains($name));
    }
    elsif $opcodes {
        show $M.frames.grep($grepper);
    }
    elsif $decomp {
        say $M.de-compile;
    }
    else {
        say $M.gist(:$verbose) unless $header;
    }
}

# vim: expandtab shiftwidth=4
