#!perl
use strict;
use warnings;
use lib 'lib';
use Devel::ebug;
use Term::ReadLine;
use YAML;

my $filename = shift;
die "Usage: ebug filename\n" unless $filename;

my $ebug = Devel::ebug->new;
$ebug->program($filename);
$ebug->load;

my $codelines;

print "* Welcome to Devel::ebug $Devel::ebug::VERSION\n";

my $term = Term::ReadLine->new('ebug');
my $last_command = "s";

while (1) {
  if ($ebug->finished) {
    print "ebug: Program finished running!\n";
    exit;
  }
  print $ebug->subroutine
    . "(" . $ebug->filename . "#" . $ebug->line . "): "
    . $ebug->codeline, "\n";
  my $command = $term->readline("ebug: ");
  $command = "q" if not defined $command;
  $command = $last_command if ($command eq "");

  if ($command eq 'h') {
    print 'Commands:

    b Set breakpoint at a line number (eg: b 6, b code.t 6, b code.t 6 $x > 7,
      b Calc::fib)
    e Eval Perl code and print the result (eg: e $x+$y)
    f Show all the filenames loaded
    l Show codelines
    n Next (steps over subroutine calls)
    p Show pad
    r Run until next break point or watch point
  ret Return from subroutine
    s Step (steps into subroutine calls)
    w Set a watchpoint (eg: w $t > 10)
    y Dump a variable using YAML (eg: d $x)
    q Quit
';
  } elsif ($command eq 'l') {
    if (not exists $codelines->{$ebug->filename}) {
      $codelines->{$ebug->filename} = [$ebug->codelines];
    }
    my @span = ($ebug->line-4 .. $ebug->line+4);
    @span = grep { $_ > 0 } @span;
    my @codelines = @{$codelines->{$ebug->filename}};
    my @break_points = $ebug->break_points();
    my %break_points;
    $break_points{$_}++ foreach @break_points;
    foreach my $s (@span) {
      my $codeline = $codelines[$s -1 ];
      next unless defined $codeline;
      if ($s == $ebug->line) {
	print "*";
      } elsif ($break_points{$s}) {
	print "b";
      } else {
	print " ";
      }
      print "$s:$codeline\n";
    }
  } elsif ($command eq 'p') {
    my $pad = $ebug->pad;
    foreach my $k (sort keys %$pad) {
      my $v = $pad->{$k};
      print "  $k = $v;\n";
    }
  } elsif ($command eq 's') {
    $ebug->step;
  } elsif ($command =~ /^e (.+)/) {
    my $v = $ebug->eval($1) || "";
    print "$v\n";
  } elsif ($command =~ /^y (.+)/) {
    my $v = $ebug->eval($1) || "";
    print Dump($v);
  } elsif ($command eq 'n') {
    $ebug->next;
  } elsif ($command eq 'r') {
    $ebug->run;
  } elsif ($command eq 'return') {
    $ebug->run;
  } elsif ($command eq 'f') {
    print "$_\n" foreach $ebug->filenames;
  } elsif (my($line, $condition) = $command =~ /^b (\d+) ?(.*)/) {
    undef $condition unless $condition;
    $ebug->break_point($line, $condition);
  } elsif ($command =~ /^b (.+?) (\d+) ?(.*)/) {
    $ebug->break_point($1, $2, $3);
  } elsif ($command =~ /^b (.+)/) {
    $ebug->break_point_subroutine($1);
  } elsif ($command =~ /^w (.+)/) {
    my($watch_point) = $command =~ /^w (.+)/;
    $ebug->watch_point($watch_point);
  } elsif ($command eq 'q') {
    exit;
  } else {
    print "Unknown ebug command '$command'!\n";
  }
  $last_command = $command;
}
