require 'net/http/server/parser'
require 'net/http/server/requests'
require 'net/http/server/responses'

require 'net/protocol'
require 'gserver'

module Net
  class HTTP < Protocol
    module Server
      class Daemon < GServer

        include Requests
        include Responses

        # Default host to bind to.
        DEFAULT_HOST = '0.0.0.0'

        # Default port to listen on.
        DEFAULT_PORT = 8080

        # Maximum number of simultaneous connections.
        MAX_CONNECTIONS = 256

        # Creates a new HTTP Daemon.
        #
        # @param [Hash] options
        #   Options for the daemon.
        #
        # @option options [String] :host (DEFAULT_HOST)
        #   The host to run on.
        #
        # @option options [String] :port (DEFAULT_PORT)
        #   The port to listen on.
        #
        # @option options [Integer] :max_connections (MAX_CONNECTIONS)
        #   The maximum number of simultaneous connections.
        #
        # @option options [IO] :log (STDERR)
        #   The log to write errors to.
        #
        # @option options [#call] :handler
        #   The HTTP Request Handler object.
        #
        # @yield [request, socket]
        #   If a block is given, it will be used to process HTTP Requests.
        #
        # @yieldparam [Hash{Symbol => String,Array,Hash}] request
        #   The HTTP Request.
        #
        # @yieldparam [TCPSocket] socket
        #   The TCP socket of the client.
        #
        def initialize(options={},&block)
          host = options.fetch(:host,DEFAULT_HOST)
          port = options.fetch(:port,DEFAULT_PORT).to_i
          max_connections = options.fetch(:max_connections,MAX_CONNECTIONS)
          log = options.fetch(:log,STDERR)

          super(port,host,max_connections,log,false,true)

          handler(options[:handler],&block)
        end

        #
        # Sets the HTTP Request Handler.
        #
        # @param [#call, nil] object
        #   The HTTP Request Handler object.
        #
        # @yield [request, socket]
        #   If a block is given, it will be used to process HTTP Requests.
        #
        # @yieldparam [Hash{Symbol => String,Array,Hash}] request
        #   The HTTP Request.
        #
        # @yieldparam [TCPSocket] socket
        #   The TCP socket of the client.
        #
        # @raise [ArgumentError]
        #   The HTTP Request Handler must respond to `#call`.
        #
        def handler(object=nil,&block)
          if object
            unless object.respond_to?(:call)
              raise(ArgumentError,"HTTP Request Handler must respond to #call")
            end
          elsif block.nil?
            raise(ArgumentError,"no HTTP Request Handler block given")
          end

          @handler = (object || block)
        end

        #
        # Receives HTTP Requests and handles them.
        #
        # @param [TCPSocket] socket
        #   A new TCP connection.
        #
        def serve(socket)
          if (raw_request = read_request(socket))
            parser = Parser.new

            begin
              request = parser.parse(raw_request)
            rescue Parslet::ParseFailed => error
              return Responses::BAD_REQUEST
            end

            normalize_request(request)

            # rack compliant
            status, headers, body = @handler.call(request,socket)

            write_response(socket,status,headers,body)
          end
        end

      end
    end
  end
end