# -*- coding: utf-8 -*-
# Copyright (C) 2010-2011, 2015 Rocky Bernstein <rockyb@rubyforge.net>
require_relative '../command'
class Trepan::Command::BacktraceCommand < Trepan::Command

  unless defined?(HELP)
    NAME          = File.basename(__FILE__, '.rb')
    HELP = <<-HELP
**#{NAME}** [*count*]

Print a stack trace, with the most recent frame at the top.  With a
positive number, print at most many entries.  With a negative number
print the top entries minus that number.

In the listing produced, an arrow `-->` indicates the 'current
frame'. The current frame determines the context used for many
debugger commands such as in expression evaluation, `eval`, or in
source-line listing, `list`, or the `edit` command.

Examples:
---------

    #{NAME}    # Print a full stack trace
    #{NAME} 2  # Print only the top two entries
    #{NAME} -1 # Print a stack trace except the initial (least recent) call."

See also:
---------

`up`, `down`, `frame`.
HELP

    ALIASES       = %w(bt where)
    CATEGORY      = 'stack'
    MAX_ARGS      = 1  # Need at most this many
    NEED_STACK    = true
    SHORT_HELP    = 'Print backtrace of stack frames'
  end

  require_relative '../../app/frame'
  include Trepan::Frame

  def complete(prefix)
    @proc.frame_complete(prefix, nil)
  end

  # This method runs the command
  def run(args)
    unless @proc.frame
      errmsg 'No frame.'
      return false
    end
    hide_level  =
      if (!settings[:hidelevel] || settings[:hidelevel] < 0) &&
          @proc.hidelevels[Thread.current]
        @proc.hidelevels[Thread.current]
      else settings[:hidelevel]
      end
    stack_size = @proc.top_frame.stack_size - hide_level
    opts = {
      :basename    => settings[:basename],
      :current_pos => @proc.frame_index,
      :maxstack    => settings[:maxstack],
      :maxwidth    => settings[:maxwidth],
      :show_pc     => settings[:show_pc]
    }
    opts[:count] =
      if args.size > 1
        opts[:maxstack] = @proc.get_int(args[1],
                                       :cmdname   => self.name,
                                       :max_value => stack_size)
      else
        stack_size
      end
    return false unless opts[:count]
    # FIXME: Fix Ruby so we don't need this workaround?
    # See also location.rb
    opts[:class] = @proc.core.hook_arg  if
      'CFUNC' == @proc.frame.type &&
      @proc.core.hook_arg && @proc.event != 'raise'
    print_stack_trace(@proc.top_frame, opts)
  end
end

if __FILE__ == $0
  # Demo it.
  require 'thread_frame'
  require_relative '../mock'
  dbgr, cmd = MockDebugger::setup

  def run_cmd(cmd, args)
    cmd.run(args)
    puts '=' * 40
  end

  run_cmd(cmd, [cmd.name])

  %w(1 100).each {|count| run_cmd(cmd, [cmd.name, count])}
  cmd.settings[:basename] = true
  def foo(cmd, name)
    cmd.proc.frame_setup(RubyVM::Frame::current)
    run_cmd(cmd, [cmd.name])
  end
  foo(cmd, cmd.name)
  cmd.settings[:show_pc] = true
  1.times {run_cmd(cmd, [cmd.name])}
end
