require 'apn/base'

module APN
  # Encapsulates data returned from the {APN Feedback Service}[http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3].
  # Possesses +timestamp+ and +token+ attributes.
  class FeedbackItem
    attr_accessor :timestamp, :token

    def initialize(time, token)
      @timestamp = time
      @token = token
    end

    # For convenience, return the token on to_s
    def to_s
      token
    end
  end
  
  # When supplied with the certificate path and the desired environment, connects to the {APN Feedback Service}[http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3]
  # and returns any response as an array of APN::FeedbackItem elements.
  # 
  # See README for usage and details.
  class Feedback
    include APN::Base
        
    # Returns array of APN::FeedbackItem elements read from Apple. Connects to Apple once and caches the
    # data, continues to returns cached data unless called with <code>data(true)</code>, which clears the
    # existing feedback array.  Note that once you force resetting the cache you loose all previous feedback,
    # so be sure you've already processed it.
    def data(force = nil)
      @feedback = nil if force
      @feedback ||= receive
    end
    
    # Wrapper around +data+ returning just an array of token strings.
    def tokens(force = nil)
      data(force).map(&:token)
    end
    
    # Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
    def inspect
      "#<#{self.class.name}: #{to_s}>"
    end
    
    # Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
    def to_s
      "#{@socket ? 'Connected' : 'Connection not currently established'} to #{apn_host} on #{apn_port}"
    end
    
    protected
        
    # Connects to Apple's Feedback Service and checks if there's anything there for us.  
    # Returns an array of APN::FeedbackItem pairs
    def receive
      feedback = []

      # Hi Apple
      setup_connection

      # Unpacking code borrowed from http://github.com/jpoz/APNS/blob/master/lib/apns/core.rb
      while bunch = socket.read(38)   # Read data from the socket
        f = bunch.strip.unpack('N1n1H140')
        feedback << APN::FeedbackItem.new(Time.at(f[0]), f[2])
      end

      # Bye Apple
      teardown_connection

      return feedback
    end
    
    
    def apn_host
      @apn_host ||= apn_sandbox? ? "feedback.sandbox.push.apple.com" : "feedback.push.apple.com"
    end
    
    def apn_port
      2196
    end
    
  end
end



__END__
# Testing from irb
irb -r 'lib/apn/feedback'

a=APN::Feedback.new(:cert_path => '/Users/kali/Code/insurrection/certs/', :environment => :production)