# -*- coding: utf-8 -*-
# Copyright (C) 2011-2012, 2014 Rocky Bernstein <rocky@cpan.org>
use warnings; no warnings 'redefine';

use rlib '../../../..';

# require_relative '../../app/condition'

package Devel::Trepan::CmdProcessor::Command::Step;

use if !@ISA, Devel::Trepan::CmdProcessor::Command ;

unless (@ISA) {
    eval <<'EOE';
use constant ALIASES  => qw(s step+ step- s+ s-);
use constant CATEGORY => 'running';
use constant SHORT_HELP => 'Step program (possibly entering called functions)';
use constant MIN_ARGS   => 0;     # Need at least this many
use constant MAX_ARGS   => undef; # Need at most this many - undef -> unlimited.
use constant NEED_STACK => 1;
EOE
}

use strict;
use vars qw(@ISA); @ISA = @CMD_ISA;
use vars @CMD_VARS;  # Value inherited from parent

$NAME = set_name();
=pod

=head2 Synopsis:

=cut
$HELP = <<'HELP';
=pod

B<step>[B<+>|B<->] [B<into>] [I<count>]

B<step over>

B<step out>

Execute the current line, stopping at the next event.  Sometimes this
is called "step into".

With an integer argument, step that many times.

A suffix of C<+> in a command or an alias forces a move to another
position, while a suffix of C<-> disables this requirement.  A suffix
of C<E<gt>> will continue until the next call. (C<finish> will run  until
the return for that call.)

If no suffix is given, the debugger setting C<different> determines
this behavior.

=head2 Examples:

 step        # step 1 event, any event obeying 'set different' setting
 step 1      # same as above
 step+       # same but force stopping on a new line
 step-       # same but force stopping on a new line a new frame added
 step over   # same as 'next'
 step out    # same as 'finish'

=head2 See also:

L<C<next> (step over)|Devel::Trepan::CmdProcessor::Command::Step>,
L<C<finish> (step out)|Devel::Trepan::CmdProcessor::Command::Finish>,
L<C<continue>|Devel::Trepan::CmdProcessor::Command::Continue>, and
L<C<set different>|Devel::Trepan::CmdProcessor::Command::Set::Different>.

=cut
HELP

my $Keyword_to_related_cmd = {
    'out'  => 'finish',
    'over' => 'next',
    'into' => 'step',
};

#  include Trepan::Condition

# This method runs the command
sub run($$) {
    my ($self, $args) = @_;

    my $proc = $self->{proc};
    my $opts = $proc->parse_next_step_suffix($args->[0]);
    # condition = nil
    if (0 == $#$args) {
        # Form is: "step" which means "step 1"
        $proc->{skip_count} = 0;
    } else {
        my $replace_cmd = $Keyword_to_related_cmd->{$args->[1]};
        if (defined($replace_cmd)) {
            my $cmd = $proc->{commands}{$replace_cmd};
            return $cmd->run( [$replace_cmd, splice(@$args, 2)] );
    #   } elsif ('until' eq $args->[1]) {
    #     my $try_condition = join(@$args[2..-1], ' ');
    #     if (valid_condition?(try_condition)) {
    #       $condition = $try_condition;
    #       $opts-{different_pos} = 0;
    #       $proc->{skip_count} = 0;
    #     }
    #   elsif('to' eq $args[1]) {
    #     if args.size != 3;
    #       $self->errmsg('Expecting a method name after "to"');
    #       return;
    #     elsif (!@proc.method?(args[2])) {
    #       $self->errmsg("${args[2]} doesn't seem to be a method name");
    #       return;
    #     } else {
    #       $opts->{to_method{ = $args->[2];
    #       $opts->{different_pos} = 0;
    #       skip_count = 0
    #     }
    #   } elsif ('thread' eq $args->[1]) {
    #     $condition = "Thread.current.object_id == ${Thread.current.object_id}"
    #     $opts[:different_pos] = 0;
    #     $proc->{skip_count] = 0;
        } else {
            my $count_str = $args->[1];
            my $int_opts = {
                msg_on_error =>
                    "The 'step' command argument must eval to an integer. Got: ${count_str}",
                    min_value => 1
            };
            #     }.merge(opts)
            my $count = $proc->get_an_int($count_str, $int_opts);
            return unless defined($count);
            # step 1 is $proc->{skip_count} = 0 or "stop next event"
            $proc->{skip_count} = $count - 1  ;
        }
    }
    $proc->step($opts)
}

unless (caller) {
  # require_relative '../mock'
  # dbgr, cmd = MockDebugger::setup
  # p cmd.run([cmd.name])
}

1;
