module God
  module Conditions
    
    # Condition Symbol :simple_response
    # Type: Poll
    # 
    # Trigger based on the response data
    #
    # Paramaters
    #   Required
    #     +host+ is the hostname to connect [required]
    #     --one of code_is or code_is_not--
    #     +data_is+ trigger if the response code IS
    #     +data_is_not+ trigger if the response code IS NOT
    #     +data_contains+ trigger if the data CONTAINS
    #     +data_without+ trigger if the data DOES NOT CONTAIN
    #  Optional
    #     +port+ is the port to connect (default 80)
    #     +type+ either tcp or udp
    #     +times+ is the number of times after which to trigger (default 1)
    #             e.g. 3 (times in a row) or [3, 5] (three out of fives times)
    #     +timeout+ is the time to wait for a connection (default 60.seconds)
    #
    # Examples
    #
    # Trigger if the response code from www.example.com
    # is not "foo"  (or if the connection is refused or times out:
    #
    #   on.condition(:simple_response) do |c|
    #     c.host = 'www.example.com'
    #     c.data_is_not = "foo"
    #   end
    class SimpleResponse < PollCondition
      attr_accessor :data_is,
										:data_is_not,
										:data_contains,
										:data_without,
                    :times,        # e.g. 3 or [3, 5]
                    :host,         # e.g. www.example.com
                    :port,         # e.g. 8080
										:type,
                    :timeout      # e.g. 60.seconds
      
      def initialize
        super
				self.type = 'udp'
        self.times = [1, 1]
        self.timeout = 10.seconds
      end
      
      def prepare
        if self.times.kind_of?(Integer)
          self.times = [self.times, self.times]
        end
        
        @timeline = Timeline.new(self.times[1])
        @history = Timeline.new(self.times[1])
      end
      
      def reset
        @timeline.clear
        @history.clear
      end
      
      def valid?
        valid = true
	valid &= complain("Attribute 'type' must be either 'tcp' or 'udp'", self) if (self.type != 'tcp' && self.type != 'udp')
        valid &= complain("Attribute 'host' must be specified", self) if self.host.nil?
        valid &= complain("One (and only one) of attributes 'data_is', 'data_is_not', 'data_contains', and 'data_without' must be specified", self) if
          (self.data_is.nil? && self.data_contains.nil? && self.data_is_not.nil? && self.data_without.nil?) || (self.data_is && self.data_contains) || (self.data_is && self.data_is_not) || (self.data_is && self.data_without) || (self.data_contains && self.data_is_not) || (self.data_contains && self.data_without) || (self.data_is_not && self.data_without)
        valid
      end
      
      def test
        response = nil
        
				if self.type == 'tcp'
					sock = TCPSocket.new(self.host, self.port)
				else
					sock = UDPSocket.new()
					sock.connect(self.host, self.port)
				end

				response = sock.readlines.join(' ')


        if self.data_is && self.data_is.include?(response)
          pass(response)
        elsif self.data_contains && response =~ /#{self.data_contains}/
          pass(response)
        elsif self.data_is_not && self.data_is_not.include?(response)
          fail(response)
        elsif self.data_without && response =~ /#{self.data_without}/
          fail(response)
        else
          pass(response)
        end
      rescue Errno::ECONNREFUSED
        fail('Refused')
      rescue Errno::ECONNRESET
        fail('Reset')
      rescue EOFError
        fail('EOF')
      rescue Timeout::Error
        fail('Timeout')
      rescue Errno::ETIMEDOUT
        fail('Timedout')
      rescue Exception => failure
        fail(failure.class.name)
      end
      
      private
      
      def pass(data)
        @timeline << true
        self.info = "response nominal [#{data}]"
        false
      end
      
      def fail(data)
        @timeline << false
        self.info = "response abnormal [#{data}]"
        true
      end
      
    end
    
  end
end