require_relative "values"

module Vm
  
  # Think flowcharts: blocks are the boxes. The smallest unit of linear code
  
  # Blocks must end in control instructions (jump/call/return). 
  # And the only valid argument for a jump is a Block 
  
  # Blocks form a linked list
  
  # There are four ways for a block to get data (to work on)
  # - hard coded constants (embedded in code)
  # - memory move
  # - values passed in (from previous blocks. ie local variables)

  # See Value description on how to create code/instructions
  
  # Codes then get assembled into bytes (after linking)
  
  class Block < Code

    def initialize(name , function , next_block )
      super()
      @function = function
      @name = name.to_sym
      @next = next_block
      @branch = nil
      @codes = []
      # keeping track of register usage, left (assigns) or right (uses)
      @assigns = []
      @uses = []
    end

    attr_reader :name  , :next , :codes , :function , :assigns , :uses
    attr_accessor :branch
    
    def reachable ret = []
      add_next ret
      add_branch ret
      ret
    end

    def add_code kode
      kode.assigns.each { |a| (@assigns << a) unless @assigns.include?(a) }
      kode.uses.each { |use| (@uses << use) unless (@assigns.include?(use) or @uses.include?(use)) }
      #puts "IN ADD #{name}#{uses}" 
      @codes << kode
    end

    def set_next next_b 
      @next = next_b
    end

    # returns if this is a block that ends in a call (and thus needs local variable handling)
    def call_block?
      return false unless codes.last.is_a?(CallInstruction)
      return false unless codes.last.opcode == :call
      codes.dup.reverse.find{ |c| c.is_a? StackInstruction }
    end

    # Code interface follows. Note position is inheitted as is from Code

    # length of the block is the length of it's codes, plus any next block (ie no branch follower)
    #  Note, the next is in effect a linked list and as such may have many blocks behind it.
    def length
      cods = @codes.inject(0) {| sum  , item | sum + item.length}
      cods += @next.length if @next
      cods
    end

    # to link we link the codes (instructions), plus any next in line block (non- branched)
    def link_at pos , context
      super(pos , context)
      @codes.each do |code|
        code.link_at(pos , context)
        pos += code.length
      end
      if @next
        @next.link_at pos , context
        pos += @next.length
      end
      pos
    end

    # assemble the codes (instructions) and any next in line block
    def assemble(io)
      @codes.each do |obj|
        obj.assemble io
      end
      @next.assemble(io) if @next
    end

    private
    # helper for determining reachable blocks 
    def add_next ret
      return if @next.nil?
      return if ret.include? @next
      ret << @next
      @next.reachable ret
    end
    # helper for determining reachable blocks 
    def add_branch ret
      return if @branch.nil?
      return if ret.include? @branch
      ret << @branch
      @branch.reachable ret
    end
  end
end