#
# shodan-ruby - A Ruby interface to SHODAN, a computer search engine.
#
# Copyright (c) 2009 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

require 'shodan/host'

module Shodan
  class Page < Array

    #
    # Creates a new Page object.
    #
    # @param [Array] hosts
    #   The initial hosts.
    #
    # @yield [page]
    #   If a block is given, it will be passed the page.
    #
    # @yieldparam [Page] page
    #   The newly created Page object.
    #
    def initialize(hosts=[],&block)
      super(hosts)

      block.call(self) if block
    end

    #
    # Maps the hosts within the page.
    #
    # @yield [host]
    #   The given block will be used to map each host in the page.
    #
    # @yieldparam [Host] host
    #   A host within the page.
    #
    # @return [Array]
    #   The resulting mapped Array.
    #
    # @example
    #   page.map
    #   # => #<Page: ...>
    #
    # @example
    #   page.map { |host| host.ip }
    #   # => [...]
    #
    def map(&block)
      return self unless block

      mapped = []

      each { |element| mapped << block.call(element) }
      return mapped
    end

    #
    # Selects the hosts within the page.
    #
    # @yield [host]
    #   The given block will be used to select hosts from the page.
    #
    # @yieldparam [Host] host
    #   A host in the page.
    #
    # @return [Page]
    #   A sub-set of the hosts within the page.
    #
    # @example
    #   page.select { |host| host.headers['Server'] =~ /IIS/ }
    #
    def select(&block)
      self.class.new(super(&block))
    end

    alias hosts_with select

    #
    # Iterates over the IP addresses of every host in the page.
    #
    # @yield [ip]
    #   If a block is given, it will be passed the IP addresses of every
    #   host.
    #
    # @yieldparam [String] ip
    #   An IP address of a host.
    #
    # @return [self]
    #
    def each_ip(&block)
      each do |host|
        block.call(host.ip) if block
      end
    end

    #
    # Selects the hosts with the matching IP address.
    #
    # @param [Regexp, String] ip
    #   The IP address to search for.
    #
    # @yield [host]
    #   If a block is also given, it will be passed every matching host.
    #
    # @yieldparam [Host] host
    #   A host with the matching IP address.
    #
    # @return [Array<Host>]
    #   The hosts with the matching IP address.
    #
    def hosts_with_ip(ip,&block)
      hosts_with do |host|
        if host.ip.match(ip)
          block.call(host) if block
          
          true
        end
      end
    end

    #
    # The IP addresses of the hosts in the page.
    #
    # @return [Array<String>]
    #   The IP addresses.
    #
    def ips
      Enumerator.new(self,:each_ip).to_a
    end

    #
    # Iterates over the host names of every host in the page.
    #
    # @yield [hostname]
    #   If a block is given, it will be passed the host names of every host.
    #
    # @yieldparam [String] hostname
    #   A host name.
    #
    # @return [self]
    #
    def each_hostname(&block)
      each do |host|
        block.call(host.hostname) if (block && host.hostname)
      end
    end

    #
    # Selects the hosts with the matching host name.
    #
    # @param [Regexp, String] hostname
    #   The host name to search for.
    #
    # @yield [host]
    #   If a block is also given, it will be passed every matching host.
    #
    # @yieldparam [Host] host
    #   A host with the matching host name.
    #
    # @return [Array<Host>]
    #   The hosts with the matching host name.
    #
    def hosts_with_name(name,&block)
      hosts_with do |host|
        if (host.hostname && host.hostname.match(name))
          block.call(host) if block

          true
        end
      end
    end

    #
    # The names of the hosts in the page.
    #
    # @return [Array<String>]
    #   The host names.
    #
    def hostnames
      Enumerator.new(self,:each_hostname).to_a
    end

    #
    # Iterates over the dates that each host was added.
    #
    # @yield [date]
    #   If a block is given, it will be passed the dates that each host was
    #   added.
    #
    # @yieldparam [Date] date
    #   A date that a host was added.
    #
    # @return [self]
    #
    def each_date(&block)
      each do |host|
        block.call(host.date) if block
      end
    end

    #
    # The dates that the hosts were added.
    #
    # @return [Array<Date>]
    #   The dates.
    #
    def dates
      Enumerator.new(self,:each_date).to_a
    end

    #
    # Iterates over the responses of each host in the page.
    #
    # @yield [response]
    #   If a block is given, it will be passed the responses of each host.
    #
    # @yieldparam [String] response
    #   The initial response of a host.
    #
    # @return [self]
    #
    def each_response(&block)
      each do |host|
        block.call(host.response) if block
      end
    end

    #
    # Selects the hosts with the matching response.
    #
    # @param [Regexp, String] pattern
    #   The response pattern to search for.
    #
    # @yield [host]
    #   If a block is also given, it will be passed every matching host.
    #
    # @yieldparam [Host] host
    #   A host with the matching response.
    #
    # @return [Array<Host>]
    #   The hosts with the matching response.
    #
    def responses_with(pattern,&block)
      hosts_with do |host|
        if host.response.match(pattern)
          block.call(host) if block

          true
        end
      end
    end

    #
    # The responses of the hosts in the page.
    #
    # @return [Array<String>]
    #   The responses.
    #
    def responses(&block)
      Enumerator.new(self,:each_response).to_a
    end

    #
    # Itereates over the HTTP headers of each host in the page.
    #
    # @yield [headers]
    #   If a block is given, it will be passed the headers from each host
    #   in the page.
    #
    # @yieldparam [Hash] headers
    #   The headers from a host in the page.
    #
    # @return [self]
    #
    def each_http_headers(&block)
      each do |host|
        block.call(host.http_headers) if block
      end
    end

    #
    # The HTTP headers from the hosts in the page.
    #
    # @return [Array<Hash>]
    #   The HTTP headers.
    #
    def http_headers
      Enumerator.new(self,:each_http_headers).to_a
    end

  end
end