require "ftw"
require "ftw/protocol"
require "ftw/crlf"
require "socket"
require "cabin"

# An attempt to invent a simple FTW web server.
class FTW::WebServer
  include FTW::Protocol
  include FTW::CRLF

  def initialize(host, port, &block)
    @host = host
    @port = port
    @handler = block

    @logger = Cabin::Channel.get
    @threads = []
  end # def initialize

  # Run the server.
  #
  # Connections are farmed out to threads.
  def run
    logger.info("Starting server", :config => @config)
    @server = FTW::Server.new([@host, @port].join(":"))
    @server.each_connection do |connection|
      @threads << Thread.new do
        handle_connection(connection)
      end
    end
  end # def run

  def stop
    @server.stop unless @server.nil?
    @threads.each(&:join)
  end # def stop

  # Handle a new connection.
  #
  # This method parses http requests and passes them on to #handle_request
  #
  # @param connection The FTW::Connection being handled.
  def handle_connection(connection)
    while true
      begin
        request = read_http_message(connection)
      rescue EOFError, Errno::EPIPE, Errno::ECONNRESET, HTTP::Parser::Error, IOError
        # Connection EOF'd or errored before we finished reading a full HTTP
        # message, shut it down.
        break
      rescue FTW::HTTP::Message::UnsupportedHTTPVersion
        break
      end

      if request["Content-Length"] || request["Transfer-Encoding"]
        request.body = connection
      end

      begin
        handle_request(request, connection)
      rescue => e
        puts e.inspect
        puts e.backtrace
        raise e
      end
    end
    connection.disconnect("Fun")
  end # def handle_connection

  # Handle a request. This will set up the rack 'env' and invoke the
  # application associated with this handler.
  def handle_request(request, connection)
    response = FTW::Response.new
    response.version = request.version
    response["Connection"] = request.headers["Connection"] || "close"

    # Process this request with the handler
    @handler.call(request, response, connection)

    # Write the response
    begin
      connection.write(response.to_s + CRLF)
      if response.body?
        write_http_body(response.body, connection,
                        response["Transfer-Encoding"] == "chunked") 
      end
    rescue => e
      @logger.error(e)
      connection.disconnect(e.inspect)
    end

    if response["Connection"] == "close" or response["Connection"].nil?
      connection.disconnect("'Connection' header was close or nil")
    end
  end # def handle_request

  # Get the logger.
  def logger
    if @logger.nil?
      @logger = Cabin::Channel.get
    end
    return @logger
  end # def logger

  public(:run, :initialize, :stop)
end # class FTW::WebServer
