module Caricature

  # A proxy to CLR objects that records method calls.
  # this implements all the public instance methods of the class when you use it in ruby code
  # When you use it in a CLR class you're bound to CLR rules and it only overrides the methods
  # that are marked as virtual. We also can't isolate static or sealed types at the moment.
  class ClrIsolator < Isolator


    # Implementation of the template method that creates
    # an isolator for a class defined in a CLR language.
    def initialize(context)
      super
      instance = nil
      sklass = context.subject
      unless context.subject.respond_to?(:class_eval)
        sklass = context.subject.class
        instance = context.subject
      end
      @descriptor = ClrClassDescriptor.new sklass
      instance ||= sklass.new unless sklass.to_clr_type.is_abstract
      build_isolation sklass, instance
    end

    # initializes the messaging strategy for the isolator
    def initialize_messenger
      @context.messenger = ClrClassMessenger.new @context.expectations, @subject
    end

    # builds the Isolator class for the specified subject
    def  create_isolation_for(subj)
      members = @descriptor.instance_members
      class_members = @descriptor.class_members

      klass = Object.const_set(class_name(subj), Class.new(subj))
      klass.class_eval do

        include Interception

        # access to the proxied subject
        def ___super___
          isolation_context.instance
        end      
        
        def initialize(*args)  
          self                      
        end

        members.each do |mem|
          nm = mem.name.to_s.to_sym
          define_method nm do |*args|
            b = nil
            b = Proc.new { yield } if block_given?
            isolation_context.send_message(nm, mem.return_type, *args, &b)
          end unless nm == :to_string
        end

        class_members.each do |mn|
          mn = mn.name.to_s.to_sym
          define_cmethod mn do |*args|
            b = nil
            b = Proc.new { yield } if block_given?
            isolation_context.send_class_message(mn, nil, *args, &b)
          end
        end

      end

      klass
    end

  end

  # An +Isolator+ for CLR interfaces.
  # this implements all the methods that are defined on the interface.
  class ClrInterfaceIsolator < Isolator

    # Implementation of the template method that creates
    # an isolator for an interface defined in a CLR language.
    def initialize(context)
      super
      sklass = context.subject
      @descriptor = ClrInterfaceDescriptor.new sklass
      build_isolation sklass
    end

    # initializes the messaging strategy for the isolator
    def initialize_messenger
      @context.messenger = ClrInterfaceMessenger.new @context.expectations
    end

    # builds the actual +isolator+ for the CLR interface
    def create_isolation_for(subj)
      proxy_members = @descriptor.instance_members

      klass = Object.const_set(class_name(subj), Class.new)
      klass.class_eval do

        include subj
        include Interception

        proxy_members.each do |mem|
          nm = mem.name.to_s.to_sym
          define_method nm do |*args|
            b = nil
            b = Proc.new { yield } if block_given?
            isolation_context.send_message(nm, mem.return_type, *args, &b)
          end
        end

      end

      klass
    end
  end

end