The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# -*- coding: utf-8 -*-
# Copyright (C) 2011, 2012 Rocky Bernstein <rocky@cpan.org> 
use strict; use warnings;
use rlib '../../..';

use Devel::Trepan::Position;
package Devel::Trepan::CmdProcessor;
use English qw( -no_match_vars );

use constant SINGLE_STEPPING_EVENT =>  1;
use constant NEXT_STEPPING_EVENT   =>  2;
use constant DEEP_RECURSION_EVENT  =>  4;
use constant RETURN_EVENT          => 32;


# attr_accessor :stop_condition  # String or nil. When not nil
#                                # this has to eval non-nil
#                                # in order to stop.
# attr_accessor :stop_events     # Set or nil. If not nil, only
#                                # events in this set will be
#                                # considered for stopping. This is
#                                # like core.step_events (which
#                                # could be used instead), but it is
#                                # a set of event names rather than
#                                # a bitmask and it is intended to
#                                # be more temporarily changed via
#                                # "step>" or "step!" commands.
# attr_accessor :to_method

sub continue($$) {
    my ($self, $args) = @_;
    $self->{skip_count} = -1;
    if ($self->{settings}{traceprint}) {
        $self->step();
        return;
    }
    if (scalar @{$args} != 1) {
        # Form is: "continue"
        # my $(line_number, $condition, $negate) = 
        #    $self->breakpoint_position($self->{proc}{cmd_argstr}, 0);
        # return unless iseq && vm_offset;
        # $bp = $self->.breakpoint_offset($condition, $negate, 1);
        #return unless bp;
        $self->{leave_cmd_loop} = $self->{dbgr}->cont($args->[1]);
    } else {
        $self->{leave_cmd_loop} = $self->{dbgr}->cont;
    };
    if ($self->{leave_cmd_loop}) {
        $self->{DB_running} = 1;
        $self->{DB_single} =  0;
    }
}

# sub quit(cmd='quit')
# {
#     @next_level      = 32000; # I'm guessing the stack size can't ever
#                             # reach this
#     @next_thread     = undef;
#     @core.skip_count = -1;    # No more event stepping
#     @leave_cmd_loop  = 1;  # Break out of the processor command loop.
#     @settings[:autoirb] = 0;
#     @cmdloop_prehooks.delete_by_name('autoirb');
#     @commands['quit'].run([cmd]);
# }

sub parse_next_step_suffix($$)
{
    my ($self, $step_cmd) = @_;
    my $opts = {};
    my $sigil = substr($step_cmd, -1);
    if ('-' eq $sigil) {
        $opts->{different_pos} = 0;
    } elsif ('+' eq $sigil) { 
        $opts->{different_pos} = 1;
    } elsif ('=' eq $sigil) { 
        $opts->{different_pos} = $self->{settings}{different}; 
        # when ('!') { $opts->{stop_events} = {'raise' => 1} };
        # when ('<') { $opts->{stop_events} = {'return' => 1}; }
        # when ('>') { 
        #     if (length($step_cmd) > 1 && substr($step_cmd, -2, 1) eq '<')  {
        #       $opts->{stop_events} = {'return' => 1 };
        #     } else {
        #       $opts->{stop_events} = {'call' => 1; }
        #     }
        # }
    } else {
        $opts->{different_pos} = $self->{settings}{different};
    }
    return $opts;
}

# Does whatever setup needs to be done to set to ignore stepping
# to the finish of the current method.
sub finish($$) {
    my ($self, $level_count) = @_;
    $self->{leave_cmd_loop} = 1;
    $self->{dbgr}->finish($level_count);
    $self->{DB_running} = 1;
    $self->{skip_count} = -1;
}

sub next($$) 
{
    my ($self, $opts) = @_;
    $self->{different_pos} = $opts->{different_pos};
    $self->{leave_cmd_loop} = 1;
    # NEXT_STEPPING_EVENT is sometimes broken.
    # $self->{DB_single}  = NEXT_STEPPING_EVENT;
    $self->{next_level} = $self->{stack_size};
    $self->{DB_single}  = SINGLE_STEPPING_EVENT;
    $self->{DB_running} = 1;
}

sub step($$) 
{
    my ($self, $opts) = @_;
    $self->{different_pos} = $opts->{different_pos};
    $self->{leave_cmd_loop} = 1;
    $self->{DB_single}  = SINGLE_STEPPING_EVENT;
    $self->{next_level} = 30000; # Virtually infinite
    $self->{DB_running} = 1;
}

sub running_initialize($)
{
    my $self = shift;
    $self->{stop_condition}  = undef;
    $self->{stop_events}     = undef;
    $self->{to_method}       = undef;
    $self->{last_pos}        = TrepanPosition->new(pkg => '',  filename => '',
                                                   line =>'', event=>'');
}

# Should we not stop here? 
# Some reasons for skipping: 
# -  step count was given. 
# - We want to make sure we stop on a different line
# - We want to stop only when some condition is reached (step util ...). 
sub is_stepping_skip($)
{

    my $self = shift;
    if ($self->{skip_count} < 0) {
        return 1;
    } elsif ($self->{skip_count} > 0) {
        $self->{skip_count} --;
        return 1
    }

    if ($self->{settings}{'debugskip'}) {
        $self->msg("diff: $self->{different_pos}, event : $self->{event}");
        $self->msg("skip_count  : $self->{skip_count}");
    }

    my $frame = $self->{frame};

    my $new_pos = TrepanPosition->new(pkg       => $frame->{pkg}, 
                                      filename  => $frame->{file}, 
                                      line      => $frame->{line},
                                      event     => $self->{event});

    my $skip_val = 0;

    # If the last stop was a breakpoint, don't stop again if we are at
    # the same location with a line event.

    my $last_pos = $self->{last_pos};
    # $skip_val ||= ($last_pos->event eq 'brkpt' && $self->{event} eq 'line');
    
    if ($self->{settings}{'debugskip'}) {
        $self->msg("skip: $skip_val, last: $self->{last_pos}->inspect(), " . 
                   "new: $new_pos->inspect()"); 
    }

    # @last_pos[2] = new_pos[2] if 'nostack' eq $self->{different_pos};

    my $condition_met = 1;
    # if (! $skip_val) {
    #   if (@stop_condition) {
    #       puts 'stop_cond' if @settings[:'debugskip'];
    #       debug_eval_no_errmsg(@stop_condition);
    # } elsif (@to_method) {
    #   puts "method #{@frame.method} #{@to_method}" if 
    #       $self->{setting}{'debugskip'};
    #   @frame.method == @to_method;
    # } else {
    #   puts 'uncond' if $self->{settings}{'debugskip'};
    #   1;
    # };
          
    # $self->msg("condition_met: #{condition_met}, last: $self->{last_pos}, " .
    #      "new: $new_pos->inspect(), different #{@different_pos.inspect}") if 
    #          $self->{settings}{'debugskip'};

    $skip_val = (($last_pos && $last_pos->eq($new_pos) && !!$self->{different_pos}) 
                 || !$condition_met);

    $self->{last_pos} = $new_pos;

    unless ($skip_val) {
        # Set up the default values for the next time we consider
        # skipping.
        $self->{different_pos} = $self->{settings}{different};
    }

    return $skip_val;
}

sub restart_args($$) {
    my $self = shift;
    my @flags = ();
    # If warn was on before, turn it on again.
    no warnings 'once';
    push @flags, '-w' if $DB::ini_warn;

    # Rebuild the -I flags that were on the initial
    # command line.
    for (@DB::ini_INC) {
        push @flags, '-I', $_;
    }

    # Turn on taint if it was on before.
    push @flags, '-T' if ${^TAINT};

    # Arrange for setting the old INC:
    # Save the current @init_INC in the environment.
    DB::set_list( "PERLDB_INC", @DB::ini_INC );

    ( $EXECUTABLE_NAME, @flags, '-d:Trepan', $DB::ini_dollar0, 
      @{$self->{dbgr}{exec_strs}},
      @DB::ini_ARGV );
}


scalar "Just one part of the larger Devel::Trepan::CmdProcessor";