require_relative "type"
require "parfait/message"

module Positioned
  def position
    raise "position accessed but not set at #{length} for #{self.inspect[0...500]}" if @position == nil
    @position
  end
  def set_position pos
    # resetting of position used to be error, but since relink and dynamic instruction size it is ok. in measures
    if @position != nil and ((@position - pos).abs > 32)
      raise "position set again #{pos}!=#{@position} for #{self}" 
    end
    @position = pos
  end
end

module Virtual
  # our machine is made up of objects, some of which are code, some data
  #
  # during compilation objects are module Virtual objects, but during execution they are not scoped
  # 
  # So compiling/linking/assembly turns ::virtual objects into binary that represents ruby objects at runtime
  # The equivalence is listed below (i'll try and work on clearer correspondence later)
  #  ::Virtual            Runtime / parfait
  #   Object                  Object
  #   BootClass               Class
  #   MetaClass               self/Object
  #   BootSpace               ObjectSpace
  #   CompiledMethod          Function
  #   (ruby)Array             Array
  #         String            String
  class Object
    include Positioned
    def initialize
      @position = nil
      @length = -1
    end
    attr_accessor  :length , :layout
    def inspect
      Sof::Writer.write(self)
    end
    def to_s
      inspect[0..300]
    end
    def mem_length
      raise "abstract #{self.class}"
    end
    @@EMPTY =  { :names => [] , :types => []}
    def layout
      raise "Find me #{self}"
      self.class.layout
    end
    def self.layout
      @@EMPTY
    end
    # class variables to have _identical_ objects passed back (stops recursion)
    @@ARRAY =  { :names => [] , :types => []}
#    @@HASH = { :names => [:keys,:values] , :types => [Virtual::Reference,Virtual::Reference]}
#    @@CLAZZ = { :names => [:name , :super_class_name , :instance_methods] , :types => [Virtual::Reference,Virtual::Reference,Virtual::Reference]}
#    @@SPACE = { :names => [:classes,:objects] , :types => [Virtual::Reference,Virtual::Reference]}

    def layout_for(object)
      case object
      when Array , Symbol , String , Virtual::CompiledMethod , Virtual::Block , Virtual::StringConstant
        @@ARRAY
      when Hash
        @@HASH.merge :keys => object.keys , :values => object.values 
      when Virtual::BootClass
        @@CLAZZ
      when Virtual::BootSpace
        @@SPACE
      else
        raise "linker encounters unknown class #{object.class}"
      end
    end
    # objects only come in lengths of multiple of 8 words
    # but there is a constant overhead of 2 words, one for type, one for layout
    # and as we would have to subtract 1 to make it work without overhead, we now have to add 7
    def padded len
      a = 32 * (1 + (len + 7)/32 )
      #puts "#{a} for #{len}"
      a
    end

    def padded_words words
      padded(words*4) # 4 == word length, a constant waiting for a home
    end
  end
end
::Message.class_eval do
  include Positioned
  def layout
    Virtual::Object.layout
  end
  def mem_length
    Virtual::Object.new.padded_words(2)
  end
end
::Frame.class_eval do
  include Positioned
  def layout
    Virtual::Object.layout
  end
  def mem_length
    Virtual::Object.new.padded_words(2)
  end
end
Parfait::Hash.class_eval do
  include Positioned
  HASH = { :names => [:keys,:values] , :types => [Virtual::Reference,Virtual::Reference]}
  def layout
    HASH
  end
  def mem_length
    Virtual::Object.new.padded_words(2)
  end
end
Array.class_eval do
  include Positioned
  def layout
    Virtual::Object.layout
  end
  def mem_length
    Virtual::Object.new.padded_words(length())
  end
end
Symbol.class_eval do
  include Positioned
  def layout
    Virtual::Object.layout
  end
  def mem_length
    Virtual::Object.new.padded(1 + to_s.length())
  end
end
String.class_eval do
  include Positioned
  def layout
    Virtual::Object.layout
  end
  def mem_length
    Virtual::Object.new.padded(1 + length())
  end
end