###################  esp.rb -- brent@mbari.org  ####################
# $Source: /home/cvs/ESP/gen2/software/esp/lib/esp.rb,v $
# $Id: esp.rb,v 1.2 2005/06/24 18:27:10 brent Exp $
#
# ruby ESP top-level startup
#
####################################################################

require 'mbari'

def schedule (*arg)
  require 'timehash'
  TimeHash.new.add *arg
end

class File
  def self.absPath? path
  #true iff path is absolute (starts with root or ~)
    first=path[0,1]
    first=='~' || first==File::SEPARATOR    
  end
  
  def self.which (fn, paths=ENV["PATH"])
  #return the full path to fn by searching paths
  #analogous to the unix which command
    paths.split(PATH_SEPARATOR).each {|dir|
      fullPath = dir+SEPARATOR+fn
      return fullPath if FileTest.readable? (fullPath)
    }
    nil
  end
end


module ESP

  Home=ENV["ESPhome"]
  Path=ENV["ESPpath"]
  mode=ENV["ESPmode"]
  Mode=(mode.intern unless mode and mode.empty?)
  

  def self.ld fn
  # load either fn or fn.rb
    fn += ".rb" unless fn[-3,3]==".rb" || FileTest.readable? (fn)
    Kernel.load fn
  end
  
  def self.load fn
    raise TypeError,"Expected String -- not #{fn.type}" unless fn.is_a? String
    return ld (fn) if File.absPath? fn
    if fn != File.basename(fn)
      begin
        return ld fn
      rescue LoadError
        return ld (Home+File::SEPARATOR+fn)
      end
    end
    fullPath = File.which (fn, Path)
    fullPath = File.which (fn+".rb", Path) unless fullPath
    raise LoadError, "No '#{fn}' script found on ESP::Path" unless fullPath
    Kernel.load fullPath
  end
 
  def self.configureIRB appFn=__FILE__
    require 'irb'
    require 'binding'  #for IRB.breakpoint
    IRB.initialize appFn
    IRB.conf[:IRB_NAME]=IRB.conf[:AP_NAME]="ESP" 
    IRB.conf[:IGNORE_EOF]=true
    IRB.conf[:INSPECT_MODE]=:asIRBtext
    IRB.conf[:BACK_TRACE_LIMIT]=0   #no backtrace display
    IRB.conf[:VERBOSE]=true
    $RBBR.name=RBBR if defined? $RBBR
  end
  
  def self.runApplication
    if defined? IRB
      IRB.start $0
    else
      execute $0
    end
  end
  
  def self.subFn dir, fn=ESP::Mode.to_s
    ESP::Home+File::SEPARATOR+dir+File::SEPARATOR+fn
  end
  
  def self.ldMode
    ld subFn('mode')  #invokes ESP.runApplication
  end
  
  def self.logFn logPath=ENV["ESPlog"]
    logPath =
      "/var/log/#{ENV['USER']}/#{ESP::Mode}.log" unless
        logPath
    system ("mkdir -p #{File.dirname logPath}")
    logPath
  end
   
end

def execute fn
  # load and/or execute an ESP script 
  ESP.load fn
end
alias define execute


if ARGV.size == 0  || ARGV[0][0]==?-  #interactive mode
  ESP.configureIRB


  module IRB
  #normal usage is:
  #  breakpoint binding
  #drops into another IRB session with the caller's binding
  #exit (optional return value) to resume the broken thread

    @nestLevel = 0
    @nestLimit = 1
    
    def IRB.nestLevel
      @nestLevel
    end
    def IRB.nestLimit
      @nestLimit
    end
    def IRB.nestLimit= newLevel
      @nestLimit=newLevel
    end
    
    def IRB.breakpoint(workspace = nil, name="breakpoint")
      if @nestLevel >= @nestLimit
        puts "Ignoring breakpoint \"#{name}\" because nestLevel > #{@nestLimit}"
        return
      end
      puts "At #{name}: (nestLevel = #{@nestLevel += 1}) "
      workspace = IRB::WorkSpace.new workspace if workspace.is_a? Binding
      @CONF[:MAIN_CONTEXT] = (irb = IRB::Irb.new(workspace)).context

      oldTrap = trap 'INT' do
        irb.signal_handle
      end
      result = catch :IRB_EXIT do
        begin
          irb.eval_input
        rescue Exception
          irb.log_exception
          retry
        end
      end
      if oldTrap
        trap ('INT', &oldTrap)
      else
        trap ('INT', 'DEFAULT')
      end
      @nestLevel -= 1
      result
    end

    class Irb
      def log_exception
        if defined?(Log) && Log.respond_to?(:recordException)
          Log.recordException $!, @context.thread
        else
          puts $!.backtrace, $!.inspect
        end
      end
    end
  end #module IRB
  
  def breakpoint(name = "breakpoint", context = nil)
    return IRB.breakpoint(context, name) if context
    Binding.of_caller do |context|
      IRB.breakpoint(context, name)
    end
  end
  
  if ESP::Mode
    ESP.ldMode
  else
    IRB.start __FILE__
  end
else
  $0 = ARGV.shift
  if ESP::Mode
    ESP.ldMode
  else
    execute $0
  end
end



