# based on irb/completion.rb

module Kz
  module RubyCompletion
    RESERVED_WORDS = [
      "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",
    ]

    module_function
    def complete(input, bind)
      target = "#{input} DUMMY".split(/[\s><=;|&\{\(]+/)[-2]
      prefix = input[0...(input.rindex(target))]
      candidates = collect_completion_candidates(target, bind)
      candidates.compact.sort.collect do |message|
        prefix + message
      end
    end

    def collect_completion_candidates(input, bind)
      case input
      when /^(\/[^\/]*\/)\.([^.]*)$/
	# Regexp
	receiver = $1
	message = Regexp.quote($2)

	candidates = Regexp.instance_methods(true)
	select_message(receiver, message, candidates)
      when /^([^\]]*\])\.([^.]*)$/
	# Array
	receiver = $1
	message = Regexp.quote($2)

	candidates = Array.instance_methods(true)
	select_message(receiver, message, candidates)
      when /^([^\}]*\})\.([^.]*)$/
	# Proc or Hash
	receiver = $1
	message = Regexp.quote($2)

	candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
	select_message(receiver, message, candidates)
      when /^(:[^:.]*)$/
 	# Symbol
	if Symbol.respond_to?(:all_symbols)
	  sym = $1
	  candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
	  candidates.grep(/^#{sym}/)
	else
	  []
	end
      when /^::([A-Z][^:\.\(]*)$/
	# Absolute Constant or class methods
	receiver = $1
	candidates = Object.constants
	candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
      when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
	# Constant or class methods
	receiver = $1
	message = Regexp.quote($4)
	begin
	  candidates = eval("#{receiver}.constants | #{receiver}.methods", bind)
	rescue Exception
	  candidates = []
	end
	candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
      when /^(:[^:.]+)\.([^.]*)$/
	# Symbol
	receiver = $1
	message = Regexp.quote($2)

	candidates = Symbol.instance_methods(true)
	select_message(receiver, message, candidates)
      when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/
	# Numeric
	receiver = $1
	message = Regexp.quote($4)

	begin
	  candidates = eval(receiver, bind).methods
	rescue Exception
	  []
	end
	select_message(receiver, message, candidates)
      when /^((\"|\').*\2)\.([^.]*)$/
	# String
	receiver = $1
	message = Regexp.quote($3)

	candidates = String.instance_methods(true)
	select_message(receiver, message, candidates)
      when /^(\$[^.]*)$/
	candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))
      when /^((\.?[^.]+)+)\.([^.]*)$/
	# variable
	receiver = $1
	message = Regexp.quote($3)

	gv = eval("global_variables", bind)
	lv = eval("local_variables", bind)
	cv = eval("self.class.constants", bind)

	if (gv | lv | cv).include?(receiver)
	  # foo.func and foo is local var.
	  candidates = eval("#{receiver}.methods", bind)
	elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
	  # Foo::Bar.func
	  begin
	    candidates = eval("#{receiver}.methods", bind)
	  rescue Exception
	    candidates = []
	  end
	else
	  # func1.func2
	  candidates = []
	end
	select_message(receiver, message, candidates)
      else
        methods_exp = "methods | private_methods | local_variables"
        constants_exp = "self.class.constants"
        exp = "#{methods_exp} | #{constants_exp}"
	candidates = eval(exp, bind) | Object.constants

	(candidates | RESERVED_WORDS).grep(/^#{Regexp.quote(input)}/)
      end
    end

    OPERATORS = [
      "%", "&", "*", "**", "+",  "-",  "/",
      "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
      "[]", "[]=", "^",
    ]

    def select_message(receiver, message, candidates)
      candidates.grep(/^#{message}/).collect do |e|
	case e
	when /^[a-zA-Z_]/
	  receiver + "." + e
	when /^[0-9]/
	when *OPERATORS
	  # receiver + " " + e
	end
      end
    end
  end
end
