module Caricature

  # A base class to encapsulate method invocation
  class Messenger

    # contains the recorder for recording method calls
    attr_accessor :recorder

    # the real instance of the isolated subject
    # used to forward calls in partial mocks
    attr_reader :instance

    # the expecations that have been set for the isolation
    attr_reader :expectations

    # creates a new instance of this messaging strategy
    def initialize(expectations, instance=nil)
      @instance, @expectations = instance, expectations
    end

    # deliver the message to the receiving isolation
    def deliver(method_name, return_type, *args, &b)
      
      internal_deliver(:instance, method_name, return_type, *args, &b)
    end

    # deliver the message to class of the receiving isolation
    def deliver_to_class(method_name, return_type, *args, &b)
      internal_deliver(:class, method_name, return_type, *args, &b)
    end

    protected

      # template method for looking up the expectation and/or returning a value
      def internal_deliver(mode, method_name, return_type, *args, &b)
        raise NotImplementedError.new("Override in an implementing class")
      end
    
    private
    def record_call(method_name, mode, expectation, *args, &b)
      recorder.record_call method_name, mode, expectation, *args, &b if recorder
    end

  end

  # Encapsulates sending messages to Ruby isolations
  class RubyMessenger < Messenger

    protected

      # implementation of the template method for looking up the expectation and/or returning a value
      def internal_deliver(mode, method_name, return_type, *args, &b)   
        exp = expectations.find(method_name, mode, *args)
        bl = record_call(method_name, mode, exp, *args, &b)
        if exp     
          block = exp.block || b
          res = instance.__send__(method_name, *args, &block) if exp.super_before?
          res = exp.execute *args, &bl
          res = instance.__send__(method_name, *args, &block) if !exp.super_before? and exp.call_super?
          res
        else
          nil
        end
      end

  end

end