#!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";
my $list_always = 0;
my $list_lines_count = 9;

while (1) {
  if ($ebug->finished) {
    print "ebug: Program finished running!\n";
    exit;
  }

  if($list_always) {
    show_codelines($codelines, $ebug, $list_lines_count) if($list_always);
  } else {
    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 break point at a line number (eg: b 6, b code.pl 6, b code.pl 6 $x > 7,
      b Calc::fib)
    d Delete a break point (d 6, d code.pl 6)
    e Eval Perl code and print the result (eg: e $x+$y)
    f Show all the filenames loaded
    l List codelines
    L List codelines always (toggle)
    n Next (steps over subroutine calls)
    p Show pad
    r Run until next break point or watch point
  ret Return from subroutine  (eg: ret, ret 3.141)
    s Step (steps into subroutine calls)
    T Show a stack trace
    w Set a watchpoint (eg: w $t > 10)
    y Dump a variable using YAML (eg: d $x)
    q Quit
';
  } elsif ($command eq 'l') {
    show_codelines($codelines, $ebug, $list_lines_count);
  } elsif ($command eq 'L') {
    $list_always = !$list_always;
  } 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 =~ /^ret ?(.*)/) {
    $ebug->return($1);
  } elsif ($command eq 'T') {
    my @trace = $ebug->stack_trace;
    foreach my $frame (@trace) {
      print $frame->package, "->",$frame->subroutine, 
	"(", $frame->filename, "#", $frame->line, ")\n";
    }
  } 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 =~ /^d (.+?) (\d+)/) {
    $ebug->break_point_delete($1, $2);
  } elsif ($command =~ /^d (\d+)/) {
    $ebug->break_point_delete($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;
}

sub show_codelines {
  my ($codelines, $ebug, $list_lines_count) = @_;

  my $line_count = int($list_lines_count / 2);

  if (not exists $codelines->{$ebug->filename}) {
    $codelines->{$ebug->filename} = [$ebug->codelines];
  }

  my @span = ($ebug->line-$line_count .. $ebug->line+$line_count);
  @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";
  }
}

__END__

=head1 NAME

Devel::ebug - A simple, extensible Perl debugger

=head1 SYNOPSIS

  % ebug calc.pl

=head1 DESCRIPTION

ebug is an interactive commmand-line front end to L<Devel::ebug>. It
is a simple Perl debugger, much like perl5db.pl.

=head1 SEE ALSO

L<Devel::ebug>

=head1 AUTHOR

Leon Brocard, C<< <acme@astray.com> >>

=head1 COPYRIGHT

Copyright (C) 2005, Leon Brocard

This program is free software; you can redistribute it or modify it
under the same terms as Perl itself.
