# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
require_relative 'virtual'

class Trepan::CmdProcessor < Trepan::VirtualCmdProcessor
  # Command processor hooks.
  attr_reader   :autoirb_hook
  attr_reader   :autolist_hook
  attr_reader   :debug_dbgr_hook
  attr_reader   :display_hook
  attr_reader   :timer_hook
  attr_reader   :trace_hook
  attr_reader   :tracebuf_hook
  attr_reader   :unconditional_prehooks
  attr_reader   :cmdloop_posthooks
  attr_reader   :cmdloop_prehooks

  # Used to time how long a debugger action takes
  attr_accessor :time_last

  class Hook
    attr_accessor :list

    def initialize(list=[])
      @list = list
    end

    def delete_by_name(delete_name)
      @list.delete_if {|hook_name, priority, hook| hook_name == delete_name}
    end

    def empty?
      @list.empty?
    end

    def insert(priority, name, hook)
      insert_loc = @list.size  # at end
      @list.each_with_index do |entry, index|
        n, p, h = entry
        if priority > p
          insert_loc = index
          break
        end
      end
      @list.insert(insert_loc, [name, priority, hook])
    end

    def insert_if_new(priority, name, hook)
      insert(priority, name, hook) unless
        @list.find {|try_name, try_priority, try_hook| try_name == name}
    end

    # Run each function in `hooks' with args
    def run(*args)
      @list.each do |name, priority, hook|
        hook.call(name, *args)
      end
    end

    # Could add delete_at and delete if necessary.
  end

  def hook_initialize(commands)
    @cmdloop_posthooks      = Hook.new
    @cmdloop_prehooks       = Hook.new
    @unconditional_prehooks = Hook.new

    irb_cmd = commands['shell']
    @autoirb_hook  = ['autoirb',
                      Proc.new{|*args| irb_cmd.run(['shell']) if irb_cmd}]

    @debug_dbgr_hook = ['dbgdbgr',
                        Proc.new{|*args|
                          if settings[:debugdbgr]
                            $trepan_cmdproc  = self
                            $trepan_frame    = @frame
                          else
                            $trepan_cmdproc  = nil
                            $trepan_frame    = nil
                          end}]

    display_cmd = commands['display']
    @display_hook   = ['display',
                       Proc.new{|*args| display_cmd.run(['display']) if
                         display_cmd}]

    list_cmd = commands['list']
    @autolist_hook  = ['autolist',
                       Proc.new{|*args| list_cmd.run(['list']) if list_cmd}]

    @timer_hook     = ['timer',
                       Proc.new{|*args|
                         now = Time.now
                         msg("%g seconds" %
                             (now - @time_last)) if @time_last
                         @time_last = now
                       }]
    @timer_posthook = ['timer', Proc.new{|*args| @time_last = Time.now}]
    @trace_hook     = ['trace',
                       Proc.new{|*args| print_location}]
  end
end

if __FILE__ == $0
  # Demo it.
  hooks = Trepan::CmdProcessor::Hook.new
  hooks.run(5)
  hook1 = Proc.new {|name, a| puts "#{name} called with #{a}"}
  hooks = Trepan::CmdProcessor::Hook.new()
  hooks.insert(-1, 'hook1', hook1)
  p hooks.list
  hooks.insert_if_new(-1, 'hook1', hook1)
  puts '-' * 30
  p hooks.list
  hooks.run(10)
  puts '-' * 30
  hooks.insert(-1, 'hook2', hook1)
  hooks.run(20)
  puts '-' * 30
  hooks.delete_by_name('hook2')
  hooks.run(30)
  puts '-' * 30
  hooks.delete_by_name('hook1')
  hooks.run(30)
  puts '-' * 30
end
