require 'irb'
#autoload(:LS, 'ls-lib')

# IRBのcompletion.rbより
module ApIRB

  class CompletionList

    ReservedWords = [
      "BEGIN", "END",
      "alias", "and", 
      "begin", "break", 
      "case", "class",
      "def", "defined", "do",
      "else", "elsif", "end", "ensure",
      "false", "for", 
      "if", "in", 
      "module", 
      "next", "nil", "not",
      "or", 
      "redo", "rescue", "retry", "return",
      "self", "super",
      "then", "true",
      "undef", "unless", "until",
      "when", "while",
      "yield"
    ]

    def get_other_statement_candidates(input)
      (get_methods_vars_consts_list | ReservedWords).grep(/^#{Regexp.quote(input)}/)
    end

    def get_methods_vars_consts_list
      eval("methods | private_methods | local_variables | type.constants",
	   IRB.conf[:MAIN_CONTEXT].workspace.binding)
    end

    # bindingなしにevalを使うと、
    # IRBで行うevalと値が変わる
    def eval_on_irb(str)
      begin
	result = eval(str, IRB.conf[:MAIN_CONTEXT].workspace.binding)
      rescue
	result = nil
      end
      result
    end

    def get_candidates_list(receiver, message, candidates, separator)
      if candidates.nil?
	return []
      end

      candidates.grep(/^#{Regexp.quote(message)}/).collect{|e| receiver + separator + e }
    end
    
    def get_candidates_of_module_methods_and_constants(receiver, message)
      if eval_on_irb("#{receiver}.type.constants.include?( %q(#{receiver}) )" )
	#p2 "const"
	if eval("#{receiver}.type") === Module
	  candidates = eval_on_irb("#{receiver}.methods|#{receiver}.constants")
	end
      end

      get_candidates_list(receiver, message, candidates, '::')
    end

    # '' でまとめるとevalでエラーを起こす確率が高い  %q を使うべき
    def get_reciever_message_candidates(receiver, message)
      #p2 receiver, message
      if eval_on_irb( "(local_variables|#{receiver}.type.constants).include?( %q(#{receiver}) )" )
	#p2 'local', "const"
	candidates = eval_on_irb("#{receiver}.methods")
      end

      get_candidates_list(receiver, message, candidates, '.')
    end

#      def get_candidates_when_method_chain(receiver, message, separator)
#        # receiverの最後が定数の場合は?
#        if eval_on_irb(receiver)
#  	candidates = eval_on_irb("#{receiver}.methods")
#        end

#        get_candidates_list(receiver, message, candidates, separator)
#      end

    # 未完成
    def get_completion_candidates(input)
      case input
      when /^([^.:]+)\.([^.]*)$/
	#p2 111
        receiver = $1
	message = $2
	get_reciever_message_candidates(receiver, message)
      when /^([^.:]+)::([^:]*)$/
	#p2 222
	receiver = $1
	message = $2
	get_candidates_of_module_methods_and_constants(receiver, message)
#        when /^(.+)(\.)([^.]*)$/ 
#  	p2 333
#  	receiver = $1
#  	sep = $2
#  	message = $3
#  	get_candidates_when_method_chain(receiver, message, sep)
#        when /^(.+)(::)([^:]*)$/
#  	p2 444
#  	receiver = $1
#  	sep = $2
#  	message = $3
#  	get_candidates_when_method_chain(receiver, message, sep)
      else
	#p2 555
	candidates = get_other_statement_candidates(input)
      end
    end

  end

  
  class IRBCompletion < ApIRB::CompletionList
    
    NONE = 0
    SINGLE = 1
    SOME = 2
    
    def initialize(editor)
      @strings = editor.lines
      @ediotr = editor
    end

    def write_completion(input, col_char_num, col_point)
      last_line = @strings.last_line
      input_word = get_input_word(input, col_point)
      list = get_completion_candidates(input_word)

      case list.size 
      when 0
	return NONE, 0
      when 1
	# そのままだと?とかでおかしくなる
	completion_str = list[0].sub(/^#{Regexp.quote(input)}/, '')

	@strings[@strings.count - 1] = last_line.concat(completion_str)
	#@strings[@strings.count - 1] = last_line[0..hoge_point].concat(completion_str).concat(last_line[hoge_point..-1])
	return SINGLE, completion_str.size
      else
	ls = LS.new(list.sort!, col_char_num)
	@strings.add(ls.get_result_string)
	@strings.add_text(last_line)
	return SOME, 0
      end
    end

    # 区切り文字の後で補完を使えるようにするため
    def get_input_word(str, col)
      #p2 str.size , col
      if str.size < col
	return nil
      end
      
      result = ''
      
      (1..col).each do |i|
	num = col - i
	
	buf = str[num..(col-1)]
	case buf
	when /^\s(\S*)/
	  result = $1
	  break
	when /^[\[\]\(\){}"',](\S*)/
	  result = $1
	  break
	end
	
	if i == col
	  result = str
	end
      end
      
      result
    end

  end

end


