require 'set'

module Marvin
  class CommandHandler < Base
    
    class_inheritable_accessor :command_prefix
    self.command_prefix  = ""
    
    @@exposed_method_mapping = Hash.new { |h,k| h[k] = [] }
    @@method_descriptions    = Hash.new { |h,k| h[k] = {} }
    
    class << self
      
      def command(name, method_desc = nil, &blk)
        exposes name
        desc method_desc unless method_desc.blank?
        define_method(name, &blk)
      end
      
      def prefix_is(p)
        self.command_prefix = p
      end
      
      def exposes(*args)
        args.each { |name| @@exposed_method_mapping[self] << name.to_sym }
      end
      
      def exposed_methods
        methods = []
        klass   = self
        while klass != Object
          methods += @@exposed_method_mapping[klass]
          klass = klass.superclass
        end
        return methods.uniq.compact
      end
      
      def prefix_regexp
        /^#{command_prefix}/
      end
      
      def desc(description)
        @last_description = description
      end
      
      def exposed_name(method)
        "#{command_prefix}#{method}"
      end
      
      def reloading!
        super
        @@exposed_method_mapping.delete(self)
        @@method_descriptions.delete(self)
      end
      
    end
    
    on_event :incoming_message, :check_for_commands
    
    def check_for_commands
      message = options.message.to_s.strip
      data, command = nil, nil
      if from_channel?
        name, command, data = message.split(/\s+/, 2)
        return if name !~ /^#{client.nickname}:/i
      else
        command, data = message.splt(/\s+/, 2)
      end
      data ||= ""
      if (command_name = extract_command_name(command)).present?
        logger.info "Processing command '#{command_name}' for #{from}"
        send(command_name, data.to_s) if respond_to?(command_name)
      end
    end
    
    def extract_command_name(command)
      re = self.class.prefix_regexp
      if command =~ re
        method_name = command.gsub(re, "").underscore.to_sym
        return method_name if self.class.exposed_methods.include?(method_name)
      end
    end
    
    def exposed_name(name)
      self.class.exposed_name(name)
    end
    
    def self.method_added(name)
      if @last_description.present?
        @@method_descriptions[self][name.to_sym] = @last_description
        @last_description = nil
      end
    end
    
  end
end