module Parfait
  # Behaviour is the old smalltalk name for the superclass of class and singleton_class
  #
  # Classes and singleton_classes are in fact very similar, in that they manage
  # - the type of instances
  # - the methods for instances
  #
  # - the instance methods are source level methods defined on the class
  # - the type refers to the instance variables and callable methods of objects
  #   (in other words the type is a concrete representation, while instance_methods
  #    is more abstract, ie source level)
  #
  # The main way they differ is that Classes manage type for a class of objects (ie many)
  # whereas singleton_class, or singleton_class manages the type of only one object
  #  (here a class)
  #
  # Singleton classes can manage the type/methods of any single object, and in the
  # future off course they will, just not yet. Most single objects don't need that,
  # only Classes and Modules _always _ do, so that's where we start.
  #
  class Behaviour < Object

    attr_reader :instance_type , :instance_methods

    def initialize(type)
      super()
      @instance_methods = List.new
      @instance_type = type
    end

    def methods
      @instance_methods
    end

    def method_names
      names = List.new
      @instance_methods.each do |method|
        names.push method.name
      end
      names
    end

    def create_instance_method_for(name , type , frame , body )
      raise "Method exists #{name}" if get_instance_method(name)
      method = Parfait::SolMethod.new(name , type , frame , body )
      add_instance_method( method )
    end

    def add_instance_method( method )
      raise "Method exists #{method.name}" if get_instance_method(method.name)
      @instance_methods.push(method)
      method
    end

    def remove_instance_method( method_name )
      found = get_instance_method( method_name )
      found ? methods.delete(found) : false
    end

    def get_instance_method( fname )
      raise "get_instance_method #{fname}.#{fname.class}" unless fname.is_a?(Symbol)
      @instance_methods.find {|m| m.name == fname }
    end

    # get the method and if not found, try superclasses. raise error if not found
    def resolve_method( m_name )
      raise "resolve_method #{m_name}.#{m_name.class}" unless m_name.is_a?(Symbol)
      method = get_instance_method(m_name)
      return method if method
      if( s_class = super_class )
        method = s_class.resolve_method(m_name)
      end
      method
    end

    # assume resolving is needed, ie getting has failed, raise if it hasnt
    def resolve_method!( m_name )
      method = get_instance_method(m_name)
      if method
        tm = @instance_type.method_names
        raise "resolve_method #{name}.#{m_name} has #{tm}:#{method_names}"
      end
      #puts "resolve #{m_name}:#{super_class}:"
      return nil unless( s_class = super_class )
      s_class.resolve_method(m_name)
    end

    # adding an instance changes the instance_type to include that variable
    def add_instance_variable( name , type)
      @instance_type = @instance_type.add_instance_variable( name , type )
    end

  end
end