require "socket"
require "interfacez/version"

module Interfacez

  # Emulated LibPcap's pcap_lookupdev function to find the default device 
  # on which to capture packets.
  def self.default
    raw_interface_addresses.each do |iface| 
      next unless iface.broadaddr
      return iface.name 
    end
    nil
  end

  # Get first (ipv4 or ipv6) loopback interface.
  def self.loopback
    Interfacez.ipv4_loopbacks { |iface| return iface }
    Interfacez.ipv6_loopbacks { |iface| return iface }
  end

  # Check for any (ipv4 or ipv6) loopback interface.
  def self.loopback?
    Interfacez.ipv4_loopbacks { |iface| return true }
    Interfacez.ipv6_loopbacks { |iface| return true }
    return false
  end

  # Get all (ipv4 or ipv6) loopback interfaces.
  def self.loopbacks
    if block_given?
      Interfacez.ipv4_loopbacks { |iface| yield iface }
      Interfacez.ipv6_loopbacks { |iface| yield iface }
    else
      loopbacks = []
      Interfacez.loopbacks { |l| loopbacks << l unless loopbacks.any?(l) }
      return loopbacks
    end
  end

  # First available ipv4 loopback interface.
  def self.ipv4_loopback
    Interfacez.ipv4_loopbacks { |iface| return iface }
  end

  # All ipv4 loopback interfaces.
  def self.ipv4_loopbacks
    if block_given?
      raw_interface_addresses.each do |iface| 
        next unless iface.addr
        next unless iface.addr.ipv4_loopback?
        yield iface.name if block_given?
      end
    else
      results = []
      Interfacez.ipv4_loopbacks { |l| results << l }
      return results 
    end
  end

  # First available ipv6 loopback interface.
  def self.ipv6_loopback
    Interfacez.ipv6_loopbacks { |iface| return iface }
  end

  # All ipv6 loopback interfaces.
  def self.ipv6_loopbacks
    if block_given?
      raw_interface_addresses.each do |iface| 
        next unless iface.addr
        next unless iface.addr.ipv6_loopback?
        yield iface.name if block_given?
      end
    else
      results = []
      Interfacez.ipv4_loopbacks { |l| results << l }
      return results 
    end
  end

  # All network interface names available on system. 
  def self.all
    addrs = raw_interface_addresses.collect { |iface| iface.name }.uniq 
    if block_given?
      addrs.each do |addr|
        yield addr
      end
    end
    return addrs
  end

  # Network interfaces with their ipv4 addresses, if they have
  # any asscoited with it.
  def self.ipv4_addresses(interface = nil)
    return ipv4_addresses_of(interface) unless interface.nil?
    results = Hash.new()
    raw_interface_addresses.each do |iface|
      next unless iface.addr
      if iface.addr.ipv4?
        results[iface.name] = [] unless results[iface.name]
        results[iface.name] << iface.addr.ip_address
      end
    end
    if block_given?
      results.each do |result|
        yield result
      end
    end
    return results
  end

  # Return all ipv4 addresses of a given interface.
  def self.ipv4_address_of(interface)
    addresses = Interfacez.ipv4_addresses[interface]
    return nil if addresses.nil?
    return addresses[0]
  end

  # Return the first ipv4 address of a given interface.
  def self.ipv4_addresses_of(interface)
    return Interfacez.ipv4_addresses[interface] || []
  end

  # Network interfaces with their ipv6 addresses, if they have
  # any asscoited with it.
  def self.ipv6_addresses(interface = nil)
    return ipv6_addresses_of(interface) unless interface.nil?
    results = Hash.new()
    raw_interface_addresses.each do |iface|
      next unless iface.addr
      if iface.addr.ipv6?
        results[iface.name] = [] unless results[iface.name]
        results[iface.name] << iface.addr.ip_address
      end
    end
    if block_given?
      results.each do |result|
        yield result
      end
    end
    return results
  end

  # Return all available ipv6 addreses of a given interface.
  def self.ipv6_addresses_of(interface)
    return Interfacez.ipv6_addresses[interface] || []
  end

  # Return the first ipv6 address of a given interface.
  def self.ipv6_address_of(interface)
    addresses = Interfacez.ipv6_addresses_of(interface)
    return nil if addresses.nil? 
    return addresses[0]
  end
  
  # Return first available mac addresses of a given interface.
  def self.mac_address_of(interface)
    list = Interfacez.mac_addresses_of(interface)
    return nil if list.size.zero?
    return list[0]
  end
  
  # Return all available mac addresses of a given interface.
  def self.mac_addresses_of(interface)
    # BSD support
    if Socket.const_defined? :PF_LINK
      list = raw_interface_addresses.map! do |iface|
        next unless iface.name == interface
        next unless iface.addr
        nameinfo = iface.addr.getnameinfo
        if nameinfo.first != "" && nameinfo.last == ""
          nameinfo[0]
        end
      end.compact
    # Linux support
    elsif Socket.const_defined? :PF_PACKET 
      list = raw_interface_addresses.map! do |iface|
        next unless iface.name == interface
        next unless iface.addr
        iface.addr.inspect_sockaddr[/hwaddr=([\h:]+)/, 1]
      end.compact
    else
      warn "This platform may not be fully supported!"
      return []
    end
  end
  
  # Get index of network interface.
  def self.index_of(interface)
    raw_interface_addresses.each do |iface|
      return iface.ifindex if iface.name == interface
    end
    return nil
  end

  # :nodoc:
  def self.raw_interface_addresses
    Socket.getifaddrs
  rescue
    warn "Unable to get raw interface address list from Socket class"
    return []
  end

end
