
require 'rest-core/util/json'

module RestCore
  class JsonResponse
    def self.members; [:json_response]; end
    include Middleware

    class ParseError < Json.const_get(:ParseError)
      attr_reader :cause, :body
      def initialize cause, body
        msg = cause.message.force_encoding('utf-8')
        super("#{msg}\nOriginal text: #{body}")
        @cause, @body = cause, body
      end
    end

    JSON_RESPONSE_HEADER = {'Accept' => 'application/json'}.freeze

    def call env, &k
      return app.call(env, &k) if env[DRY]
      return app.call(env, &k) unless json_response(env)

      app.call(env.merge(REQUEST_HEADERS =>
        JSON_RESPONSE_HEADER.merge(env[REQUEST_HEADERS]||{}))){ |response|
          yield(process(response))
        }
    end

    def process response
      body = response[RESPONSE_BODY]
      json = if body.kind_of?(String)
               Json.normalize(body)
             else
               # Yajl supports streaming, so let's pass it directly to make
               # it possible to do streaming here. Although indeed we should
               # use RESPONSE_SOCKET in this case, but doing that could
               # introduce some incompatibility which I don't want to take
               # care of for now.
               body
             end

      response.merge(RESPONSE_BODY => json && Json.decode(json))
    rescue Json.const_get(:ParseError) => error
      fail(response, ParseError.new(error, body))
    end
  end
end
