# -*- encoding: utf-8 -*-
# vim: sw=2 ts=2

require 'stringio'

module Qiniu
  module Storage
    class << self
      include Utils

      def upload_with_token(uptoken,
                            local_file,
                            bucket,
                            key = nil,
                            mime_type = nil,
                            custom_meta = nil,
                            callback_params = nil,
                            enable_crc32_check = false,
                            rotate = nil)
        action_params = _generate_action_params(
          local_file,
          bucket,
          key,
          mime_type,
          custom_meta,
          enable_crc32_check,
          rotate
        )

        if callback_params.nil?
          callback_params = {:bucket => bucket, :key => key, :mime_type => mime_type}
        end
        callback_query_string = HTTP.generate_query_string(callback_params)

        url = Config.up_host(bucket) + '/upload'
        post_data = {
          :params     => callback_query_string,
          :action     => action_params,
          :file       => File.new(local_file, 'rb'),
          :multipart  => true
        }
        if !uptoken.nil? then
          post_data[:auth] = uptoken unless uptoken.nil?
        end

        return HTTP.api_post(url, post_data)
      end # upload_with_token

      def upload_with_token_2(uptoken,
                              local_file,
                              key = nil,
                              x_vars = nil,
                              opts = {})
        ### 构造URL
        url = Config.up_host(opts[:bucket])
        url[/\/*$/] = ''
        url += '/'

        ### 构造HTTP Body
        file = File.new(local_file, 'rb')
        if not opts[:content_type].nil?
          file.define_singleton_method("content_type") do
            opts[:content_type]
          end
        end

        post_data = {
          :file      => file,
          :multipart => true,
        }
        if not uptoken.nil?
          post_data[:token] = uptoken
        end
        if not key.nil?
          post_data[:key] = key
        end
        if x_vars.is_a?(Hash)
          post_data.merge!(x_vars)
        end

        ### 发送请求
        HTTP.api_post(url, post_data)
      rescue BucketIsMissing
        raise 'upload_with_token_2 requires :bucket option when multi_region is enabled'
      end # upload_with_token_2

      def upload_buffer_with_token(uptoken,
                              buf,
                              key = nil,
                              x_vars = nil,
                              opts = {})
        ### 构造 URL
        url = Config.up_host(opts[:bucket])
        url[/\/*$/] = ''
        url += '/'

        ### 构造 HTTP Body
        if buf.is_a?(String)
          data = StringIO.new(buf)
        elsif buf.respond_to?(:read)
          data = buf
        end

        data.define_singleton_method("path") do
          'NO-PATH'
        end
        data.define_singleton_method("original_filename") do
          'A-MASS-OF-DATA'
        end
        data.define_singleton_method("content_type") do
          (opts[:content_type].nil? || opts[:content_type].empty?) ? 'application/octet-stream' : opts[:content_type]
        end

        post_data = {
          :file      => data,
          :multipart => true,
        }
        if not uptoken.nil?
          post_data[:token] = uptoken
        end
        if not key.nil?
          post_data[:key] = key
        end
        if x_vars.is_a?(Hash)
          post_data.merge!(x_vars)
        end

        ### 发送请求
        HTTP.api_post(url, post_data)
      rescue BucketIsMissing
        raise 'upload_buffer_with_token requires :bucket option when multi_region is enabled'
      end # upload_with_token_2

      ### 授权举例
      # put_policy.bucket | put_policy.key | key     | 语义 | 授权
      # :---------------- | :------------- | :------ | :--- | :---
      # trivial_bucket    | <nil>          | <nil>   | 新增 | 允许，最终key为1)使用put_policy.save_key生成的值或2)资源内容的Hash值
      # trivial_bucket    | <nil>          | foo.txt | 新增 | 允许
      # trivial_bucket    | <nil>          | bar.jpg | 新增 | 允许
      # trivial_bucket    | foo.txt        | <nil>   | 覆盖 | 允许，由SDK将put_policy.key赋值给key实现
      # trivial_bucket    | foo.txt        | foo.txt | 覆盖 | 允许
      # trivial_bucket    | foo.txt        | bar.jpg | 覆盖 | 禁止，put_policy.key与key不一致
      def upload_with_put_policy(put_policy,
                                 local_file,
                                 key = nil,
                                 x_vars = nil,
                                 opts = {})
        uptoken = Auth.generate_uptoken(put_policy)
        if key.nil? then
          key = put_policy.key
        end

        return upload_with_token_2(uptoken, local_file, key, x_vars, opts)
      rescue BucketIsMissing
        raise 'upload_with_put_policy requires :bucket option when multi_region is enabled'
      end # upload_with_put_policy

      def upload_buffer_with_put_policy(put_policy,
                                 buf,
                                 key = nil,
                                 x_vars = nil,
                                 opts = {})
        uptoken = Auth.generate_uptoken(put_policy)
        if key.nil? then
          key = put_policy.key
        end

        return upload_buffer_with_token(uptoken, buf, key, x_vars, opts)
      rescue BucketIsMissing
        raise 'upload_buffer_with_put_policy requires :bucket option when multi_region is enabled'
      end # upload_buffer_with_put_policy

      private
      def _generate_action_params(local_file,
                                  bucket,
                                  key = nil,
                                  mime_type = nil,
                                  custom_meta = nil,
                                  enable_crc32_check = false,
                                  rotate = nil)
        raise NoSuchFileError, local_file unless File.exist?(local_file)

        if key.nil?
          key = Digest::SHA1.hexdigest(local_file + Time.now.to_s)
        end

        entry_uri = bucket + ':' + key
        if mime_type.nil? || mime_type.empty?
          mime = MIME::Types.type_for local_file
          mime_type = mime.empty? ? 'application/octet-stream' : mime[0].content_type
        end

        action_params = '/rs-put/' + Utils.urlsafe_base64_encode(entry_uri) + '/mimeType/' + Utils.urlsafe_base64_encode(mime_type)
        action_params += '/meta/' + Utils.urlsafe_base64_encode(custom_meta) unless custom_meta.nil?
        action_params += '/crc32/' + Utils.crc32checksum(local_file).to_s if enable_crc32_check
        action_params += '/rotate/' + rotate if !rotate.nil? && rotate.to_i >= 0
        action_params
      end # _generate_action_params

    end # class << self
  end # module Storage
end # module Qiniu
