# encoding: utf-8
module Moped

  # Encapsulates behaviour around addresses and resolving dns.
  #
  # @since 2.0.0
  class Address

    # @!attribute host
    #   @return [ String ] The host name.
    # @!attribute ip
    #   @return [ String ] The ip address.
    # @!attribute original
    #   @return [ String ] The original host name.
    # @!attribute port
    #   @return [ Integer ] The port.
    # @!attribute resolved
    #   @return [ String ] The full resolved address.
    attr_reader :host, :ip, :original, :port, :resolved

    # Instantiate the new address.
    #
    # @example Instantiate the address.
    #   Moped::Address.new("localhost:27017")
    #
    # @param [ String ] address The host:port pair as a string.
    #
    # @since 2.0.0
    def initialize(address, timeout)
      @original = address
      @host, port = address.split(":")
      @port = (port || 27017).to_i
      @timeout = timeout
    end

    # Resolve the address for the provided node. If the address cannot be
    # resolved the node will be flagged as down.
    #
    # @example Resolve the address.
    #   address.resolve(node)
    #
    # @param [ Node ] node The node to resolve for.
    #
    # @return [ String ] The resolved address.
    #
    # @since 2.0.0
    def resolve(node)
      return @resolved if @resolved
      start = Time.now
      retries = 0
      begin
        # This timeout should be very large since Timeout::timeout plays very badly with multithreaded code
        # TODO: Remove this Timeout entirely
        Timeout::timeout(@timeout * 10) do
          Resolv.each_address(host) do |ip|
            if ip =~ Resolv::IPv4::Regex
              @ip ||= ip
              break
            end
          end
          raise Resolv::ResolvError unless @ip
        end
        @resolved = "#{ip}:#{port}"
      rescue Timeout::Error, Resolv::ResolvError, SocketError => e
        msg = ["  MOPED:", "Could not resolve IP for: #{original}, delta is #{Time.now - start}, error class is #{e.inspect}, retries is #{retries}. Node is #{node.inspect}", "n/a"]
        if retries == 0
          Loggable.info(*msg)
        else
          Loggable.warn(*msg)
        end
        if retries < 2
          retries += 1
          retry
        else
          node.down! and false
        end
      end
    end
  end
end
