
module Awestruct
  module Rack

    class App
      def initialize(doc_root)
        @doc_root = doc_root
      end

      def call(env)
        path = env['PATH_INFO']
        fs_path = File.join( @doc_root, path )

        if File.directory?(fs_path)
          if !(path =~ %r(/$))
            return [ 301,
                     { 'location'.freeze => File.join(path, '') },
                     ["Redirecting to: #{path}"] ]
          elsif File.file?(File.join(fs_path, 'index.html'))
            fs_path = File.join( fs_path, 'index.html' )
          end
        end

        # There must be a Content-Type, except when the Status is 1xx,
        # 204 or 304, in which case there must be none given.
        #
        # The Body must respond to each and must only yield String
        # values. The Body itself should not be an instance of String,
        # as this will break in Ruby 1.9.
        if File.file?(fs_path)
          body = read_content( fs_path )
          content_type = ::Rack::Mime.mime_type( File.extname(fs_path) )
          length = body.size.to_s
          [ 200,
            {'Content-Type'.freeze => content_type, 'Content-Length'.freeze => length},
            [body] ]
        else
          body, content_type = read_error_document(path)
          length = body.size.to_s
          [ 404,
            {'Content-Type'.freeze => content_type || 'text/plain', 'Content-Length'.freeze => length},
            [body] ]
        end
      end

      def read_error_document( path )
        doc_path = nil
        htaccess = File.join( @doc_root, '.htaccess' )
        if File.file?(htaccess)
          File.open( htaccess ).each_line do |line|
            if line =~ %r(^.*ErrorDocument[ \t]+404[ \t]+(.+)$)
              doc_path = $1
            end
          end
        end
        if doc_path
          fs_doc_path = File.join( @doc_root, doc_path )
          return [read_content( fs_doc_path ), ::Rack::Mime.mime_type( File.extname(fs_doc_path) )] if File.file?( fs_doc_path )
        end
        "404: Not Found: #{path}"
      end


      def read_content( path )
        input_stream = IO.open(IO.sysopen(path, 'rb'), 'rb')
        result = input_stream.read
        return result
      ensure
        input_stream.close
      end
    end

  end
end
