module SPNet

# Form a connection between an OutPort and an InPort.
#
# @author James Tunnell
class Link
  include Hashmake::HashMakeable
  
  # Define arg specs to use in processing hashed arguments during #initialize.
  ARG_SPECS = {
    :from => arg_spec(:reqd => true, :type => OutPort),
    :to => arg_spec(:reqd => true, :type => InPort)
  }
  
  attr_reader :to, :from
  
  # A new instance of Link. Link is not active by default (does not set
  # from.link and to.link to self).
  # @param [Hash] args Hashed arguments for initialization. See Link::ARG_SPECS
  #                    for details of which keys are required.
  def initialize args = {}
    hash_make args, Link::ARG_SPECS
    
    raise ArgumentError, "from port class #{@from.class} is not a #{@to.matching_class}" unless @from.is_a?(@to.matching_class)
    raise ArgumentError, "to port class #{@to.class} is not a #{@from.matching_class}" unless @to.is_a?(@from.matching_class)
  end
  
  # Make the link active by setting from.link and to.link to self.
  def activate
    @from.set_link self
    @to.set_link self
  end
  
  # Make the link inactive by setting from.link and to.link to nil.
  def deactivate
    @from.clear_link
    @to.clear_link
  end
  
  # Return true if the link is active (from.link and to.link are set to to self).
  def active?
    (@from.link == self) && (@to.link == self)
  end

  # Produce a LinkState object from the current Link object.
  def save_state blocks
    from, to = nil, nil
    
    blocks.each do |block_name, block|
      block.out_ports.each do |port_name, port|
        if port == @from
          from = PortLocater.new(:block_name => block_name, :port_name => port_name)
          break
        end
      end
      
      block.in_ports.each do |port_name, port|
        if port == @to
          to = PortLocater.new(:block_name => block_name, :port_name => port_name)
          break
        end
      end
    end
    
    raise "could not find from port" if from.nil?
    raise "could not find to port" if to.nil?
    return LinkState.new(:from => from, :to => to)
  end
end
end
