=begin
Message dispatcher, with some dummy Null Objects to facilitate testing.
=end
require 'set'

class MessageDispatcher
  # This class creates objects that will pass messages between each other.
  attr_reader :pending_messages, :incoming_messages

  def initialize
    @incoming_messages = []
    @pending_messages = []
    @recipient_types = Set.new
    @message_receivers = {}
  end

  def add_recipient_types(types)
    # Set the labels that messages will use to label themselves with.
    types.each {|type| @recipient_types.add(type.to_sym)}
  end

  def get_dispatcher_by_type(type)
    return @message_receivers[type]
  end

  def register_dispatcher(other_dispatcher, recipient_type)
    unless @recipient_types.include? recipient_type
      raise ArgumentError, "Type #{recipient_type} was not registered"
    end
    @message_receivers[recipient_type.to_sym] = other_dispatcher
  end

  def find_receiver_by_type(type)
    # Returns the objects that will respond to this kind of message.
    return nil unless @recipient_types.include? type.to_sym
    return nil unless @receivers.has_key? type.to_sym
    return @receivers[type.to_sym]
  end

  def add_new_message(recipient_type, message)
    recipient = get_dispatcher_by_type(recipient_type)
    unless recipient
      raise ArgumentError, "No known recipient with #{recipient_type}"
    end
    @pending_messages  << {
      :message => message,
      :recipient_type => recipient_type
    }
  end

  def send_all_messages
    # Sends all messages out.
    @pending_messages.each do |letter|
      recipient = @message_receivers[letter[:recipient_type]]
      message = letter[:message]
      recipient.accept_new_message(message)
    end
    @pending_messages.clear
  end

  def accept_new_message(message)
    @incoming_messages << message
  end
end
