# encoding: utf-8
module SimpleCaptcha
  class Middleware
    include SimpleCaptcha::ImageHelpers
    include SimpleCaptcha::ViewHelper

    DEFAULT_SEND_FILE_OPTIONS = {
      :type         => 'application/octet-stream'.freeze,
      :disposition  => 'attachment'.freeze,
    }.freeze

    REFRESH_FORMATS = {
      :jquery       => %Q{
          $("#%{id}").attr('src', '%{url}');
          $("#%{captcha_hidden_field_id}").attr('value', '%{key}');
      }.freeze,
      :plain_javascript => %Q{
          var img = document.getElementById("%{id}");
          if (img != null) {
            img.src = "%{url}";
          }
          var hidden = document.getElementById("%{captcha_hidden_field_id}");
          if (hidden != null) {
            hidden.value = "%{key}";
          }
      }.freeze,
      :prototype    => %Q{
          $("%{id}").setAttribute('src', '%{url}');
          $("%{captcha_hidden_field_id}").setAttribute('value', '%{key}');
      }.freeze,
    }.freeze

    def initialize(app, options={})
      @app = app
      self
    end

    def call(env) # :nodoc:
      if env["REQUEST_METHOD"] == "GET" && captcha_path?(env['PATH_INFO'])
        request = Rack::Request.new(env)
        if request.params.present? && request.params['code'].present?
          make_image(env)
        else
          refresh_code(env)
        end
      else
        @app.call(env)
      end
    end

    protected
      def make_image(env, headers = {}, status = 404)
        request = Rack::Request.new(env)
        code = request.params["code"]
        body = []

        if Utils::simple_captcha_value(code)
          #status, headers, body = @app.call(env)
          #status = 200
          #body = generate_simple_captcha_image(code)
          #headers['Content-Type'] = 'image/jpeg'
          send_file(generate_simple_captcha_image(code), :type => 'image/jpeg', :disposition => 'inline', :filename =>  'simple_captcha.jpg')
        else
          [status, headers, body]
        end
      end

      def captcha_path?(request_path)
        request_path.include?('/simple_captcha')
      end

      def send_file(path, options = {})
        raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)

        options[:filename] ||= File.basename(path) unless options[:url_based_filename]

        status = options[:status] || 200
        headers = {"Content-Disposition" => "#{options[:disposition]}; filename='#{options[:filename]}'", "Content-Type" => options[:type], 'Content-Transfer-Encoding' => 'binary', 'Cache-Control' => 'private'}
        response_body = File.open(path, "rb")

        [status, headers, response_body]
      end

      def send_data(response_body, options = {})
        status = options[:status] || 200
        headers = {"Content-Disposition" => "#{options[:disposition]}; filename='#{options[:filename]}'", "Content-Type" => options[:type], 'Content-Transfer-Encoding' => 'binary', 'Cache-Control' => 'private'}

        [status, headers, [response_body]]
      end

      def refresh_code(env)
        request = Rack::Request.new(env)

        request.session.delete :captcha
        key = simple_captcha_key(nil, request)
        options = {}
        options[:field_value] = set_simple_captcha_data(key, options)
        url = simple_captcha_image_url(key, options)

        status = 200
        id = request.params['id']
        captcha_hidden_field_id = simple_captch_hidden_field_id(id)
        format = SimpleCaptcha.refresh_format.to_sym
        raise ::ArgumentError, "Format adapter '#{format}' is not available" unless format.in?(REFRESH_FORMATS)

        body = REFRESH_FORMATS[format] % {id: id, url: url, captcha_hidden_field_id: captcha_hidden_field_id, key: key}

        headers = {'Content-Type' => 'text/javascript; charset=utf-8', "Content-Disposition" => "inline; filename='captcha.js'", "Content-Length" => body.length.to_s}.merge(SimpleCaptcha.extra_response_headers)
        [status, headers, [body]]
      end
  end
end
