# This provides online help for Ruby from within JED. It requires the ri package.
if RUBY_VERSION >= '1.9.0'
  require 'rdoc/ri/driver'
  
  class RDoc::RI::JedFormatter < RDoc::RI::AttributeFormatter
    def puts(*s)
      for i in s
	SLang.insert(i)
      end
      SLang.newline
    end
    def print(*s)
      for i in s
	SLang.insert(i)
      end
    end
    
    def initialize(*args)
      super
      @output = self
    end
    
    def draw_line(label=nil)
      len = SLang.window_info(?w.getbyte(0))[0] - 5
      len -= (label.size+1) if label
      print "-"*len
      if label
	print(" ")
	bold_print(label) 
      end
      puts
    end
    
    def wrap(txt,  prefix=@indent, linelen=SLang.window_info(?w.getbyte(0))[0] - 5)
      return unless txt && !txt.empty?
      
      txt = add_attributes_to(txt)
      next_prefix = prefix.tr("^ ", " ")
      linelen -= prefix.size
      
      line = []
      
      until txt.empty?
	word = txt.next_word
	if word.size + line.size > linelen
	  write_attribute_text(prefix, line)
	  prefix = next_prefix
	  line = []
	end
	line.concat(word)
      end
      
      write_attribute_text(prefix, line) if line.length > 0
    end
    
    
    def write_attribute_text(prefix, line)
      print prefix
      curr_attr = 0
      line.each do |achar|
	attr = achar.attr
	if achar.attr != curr_attr
	  update_attributes(achar.attr)
	  curr_attr = achar.attr
	end
	print achar.char
      end
      update_attributes(0) unless curr_attr.zero?
      puts
    end
    
    @@bold =   SLang.color_number('keyword')[0]
    
    def bold_print(txt)
      print "\033[#{@@bold}]#{txt}\033[m"
    end
    
    HEADINGS = {
      1 => ["\033[%d]" % SLang.color_number('keyword1')[0], "\033[m" ] ,
      2 => ["\033[%d]" % SLang.color_number('string')[0], "\033[m" ],
      3 => ["\033[%d]" % SLang.color_number('comment')[0], "\033[m" ]
    }
    
    def display_heading(text, level, indent)
      level = 3 if level > 3
      heading = HEADINGS[level]
      print indent
      print heading[0]
      print strip_attributes(text)
      puts heading[1]
    end
    
    private
    
    ATTR_MAP = {
      BOLD   => "%d" % SLang.color_number('keyword1')[0],
      ITALIC => "%d" % SLang.color_number('operator')[0],
      CODE   => "%d" % SLang.color_number('preprocess')[0]
    }
    
    def update_attributes(attr)
      str = "\033["
      for quality in [ BOLD, ITALIC, CODE]
	unless (attr & quality).zero?
	  str << ATTR_MAP[quality]
	end
      end
      print str, "]"
    end
  end
  
  RDoc::RI::Formatter::FORMATTERS['jed']  = RDoc::RI::JedFormatter
  
  def rubyhelp
    return RDoc::RI::Driver.new(:use_stdout => 1, :formatter => RDoc::RI::Formatter.for('jed'))
  end

else

  def puts(*s)
    for i in s
      SLang.insert(i)
    end
    SLang.newline
  end
  def print(*s)
    for i in s
      SLang.insert(i)
    end
  end
  
  require 'rdoc/ri/ri_paths'
  require 'rdoc/ri/ri_cache'
  require 'rdoc/ri/ri_util'
  require 'rdoc/ri/ri_reader'
  require 'rdoc/ri/ri_formatter'
  module RI
    
    # This is the Ansiformatter tweaked to use S-Lang escape sequences
    class JedFormatter < AttributeFormatter
      
      def initialize(*args)
	#      print "\033[0m"
	super
      end
      
      # override draw_line and wrap, the width may have changed
      def draw_line(label=nil)
	len = SLang.window_info(?w)[0] - 5
	len -= (label.size+1) if label
	print "-"*len
	if label
	  print(" ")
	  bold_print(label) 
	end
	puts
      end
      
      def wrap(txt,  prefix=@indent, linelen=SLang.window_info(?w)[0] - 5)
	return unless txt && !txt.empty?
	
	txt = add_attributes_to(txt)
	next_prefix = prefix.tr("^ ", " ")
	linelen -= prefix.size
	
	line = []
	
	until txt.empty?
	  word = txt.next_word
	  if word.size + line.size > linelen
	    write_attribute_text(prefix, line)
	    prefix = next_prefix
	    line = []
	  end
	  line.concat(word)
	end
	
	write_attribute_text(prefix, line) if line.length > 0
      end
      
      
      def write_attribute_text(prefix, line)
	print prefix
	curr_attr = 0
	line.each do |achar|
	  attr = achar.attr
	  if achar.attr != curr_attr
	    update_attributes(achar.attr)
	    curr_attr = achar.attr
	  end
	  print achar.char
	end
	update_attributes(0) unless curr_attr.zero?
	puts
      end
      
      @@bold =   SLang.color_number('keyword')[0]
      
      def bold_print(txt)
	print "\033[#{@@bold}]#{txt}\033[m"
      end
      
      HEADINGS = {
	1 => ["\033[%d]" % SLang.color_number('keyword1')[0], "\033[m" ] ,
	2 => ["\033[%d]" % SLang.color_number('string')[0], "\033[m" ],
	3 => ["\033[%d]" % SLang.color_number('comment')[0], "\033[m" ]
      }
      
      def display_heading(text, level, indent)
	level = 3 if level > 3
	heading = HEADINGS[level]
	print indent
	print heading[0]
	print strip_attributes(text)
	puts heading[1]
      end
      
      private
      
      ATTR_MAP = {
	BOLD   => "%d" % SLang.color_number('keyword1')[0],
	ITALIC => "%d" % SLang.color_number('operator')[0],
	CODE   => "%d" % SLang.color_number('preprocess')[0]
      }
      
      def update_attributes(attr)
	str = "\033["
	for quality in [ BOLD, ITALIC, CODE]
	  unless (attr & quality).zero?
	    str << ATTR_MAP[quality]
	  end
	end
	print str, "]"
      end
    end
  end
  
  ########### options #####################
  # This was copied from ri/ri_options.rb
  module RI
    
    require 'rdoc/ri/ri_display'
    
    
    class Options
      
      require 'singleton'
      require 'getoptlong'
      
      include Singleton
      
      # No not use a pager. Writable, because ri sets it if it
      # can''t find a pager
      attr_accessor :use_stdout
      
      # should we just display a class list and exit
      attr_reader :list_classes
      
      # should we display a list of all names
      attr_reader :list_names
      
      # The width of the output line
      attr_reader :width
      
      # the formatting we apply to the output
      attr_reader :formatter
      
      # the directory we search for original documentation
      attr_reader :doc_dir
      
      def initialize
	@use_stdout   = true
	@width        = SLang.window_info(?w)[0] - 5
	@formatter    = RI::JedFormatter
	@list_classes = false
	@list_names   = false
      end
      
      
      # Return the doc_dir as an array, or nil if no overriding doc dir was given
      def paths
	defined?(@doc_dir) ? [ @doc_dir ] : nil
      end
      
      # Return an instance of the displayer (the thing that actually writes
      # the information). This allows us to load in new displayer classes
      # at runtime (for example to help with IDE integration)
      
      def displayer
	::RiDisplay.new(self)
      end
    end
    
  end
  
  ########### driver #######################
  # This was copied from ri/ri_driver.rb
  
  class RiDriver
    def initialize
      @options = RI::Options.instance
      
      
      paths = RI::Paths::PATH
      if paths.empty?
	report_missing_documentation(paths)
      end
      @ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
      @display   = @options.displayer
    end
    
    # Couldn''t find documentation in paths, so tell the user
    # what to do
    
    def report_missing_documentation(paths)
      SLang.message("No ri documentation found in " + paths.join(", "))
    end
    
    
    ######################################################################
    
    # If the list of matching methods contains exactly one entry, or
    # if it contains an entry that exactly matches the requested method,
    # then display that entry, otherwise display the list of
    # matching method names
    
    def report_method_stuff(requested_method_name, methods)
      if methods.size == 1
	method = @ri_reader.get_method(methods[0])
	@display.display_method_info(method)
      else
	entries = methods.find_all {|m| m.name == requested_method_name}
	if entries.size == 1
	  method = @ri_reader.get_method(entries[0])
	  @display.display_method_info(method)
	else
	  @display.display_method_list(methods)
	end
      end
    end
    
    ######################################################################
    
    def report_class_stuff(namespaces)
      if namespaces.size == 1
	klass = @ri_reader.get_class(namespaces[0])
	@display.display_class_info(klass, @ri_reader)
      else 
	#      entries = namespaces.find_all {|m| m.full_name == requested_class_name}
	#      if entries.size == 1
	#        klass = @ri_reader.get_class(entries[0])
	#        @display.display_class_info(klass, @ri_reader)
	#      else
        @display.display_class_list(namespaces)
	#      end
      end
    end
    
    ######################################################################
    
    
    def get_info_for(arg)
      desc = NameDescriptor.new(arg)
      
      namespaces = @ri_reader.top_level_namespace
      
      for class_name in desc.class_names
	namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
	if namespaces.empty?
	  raise RiError.new("Nothing known about #{arg}")
	end
      end
      
      # at this point, if we have multiple possible namespaces, but one
      # is an exact match for our requested class, prune down to just it
      
      full_class_name = desc.full_class_name
      entries = namespaces.find_all {|m| m.full_name == full_class_name}
      namespaces = entries if entries.size == 1
      
      if desc.method_name.nil?
	report_class_stuff(namespaces)
      else
	methods = @ri_reader.find_methods(desc.method_name, 
					  desc.is_class_method,
					  namespaces)
	
	if methods.empty?
	  raise RiError.new("Nothing known about #{arg}")
	else
	  report_method_stuff(desc.method_name, methods)
	end
      end
    end
  end
  
  def rubyhelp
    return RiDriver.new
  end
  
end
