require 'thread'

module Aws
  module S3
    class BucketRegionCache

      def initialize
        @regions = {}
        @listeners = []
        @mutex = Mutex.new
      end

      # Registers a block as a callback. This listener is called when a
      # new bucket/region pair is added to the cache.
      #
      #     S3::BUCKET_REGIONS.bucket_added do |bucket_name, region_name|
      #       # ...
      #     end
      #
      # This happens when a request is made against the classic endpoint,
      # "s3.amazonaws.com" and an error is returned requiring the request
      # to be resent with Signature Version 4. At this point, multiple
      # requests are made to discover the bucket region so that a v4
      # signature can be generated.
      #
      # An application can register listeners here to avoid these extra
      # requests in the future. By constructing an {S3::Client} with
      # the proper region, a proper signature can be generated and redirects
      # avoided.
      # @return [void]
      def bucket_added(&block)
        if block
          @mutex.synchronize { @listeners << block }
        else
          raise ArgumentError, 'missing required block'
        end
      end

      # @param [String] bucket_name
      # @return [String,nil] Returns the cached region for the named bucket.
      #   Returns `nil` if the bucket is not in the cache.
      # @api private
      def [](bucket_name)
        @mutex.synchronize { @regions[bucket_name] }
      end

      # Caches a bucket's region. Calling this method will trigger each
      # of the {#bucket_added} listener callbacks.
      # @param [String] bucket_name
      # @param [String] region_name
      # @return [void]
      # @api private
      def []=(bucket_name, region_name)
        @mutex.synchronize do
          @regions[bucket_name] = region_name
          @listeners.each { |block| block.call(bucket_name, region_name) }
        end
      end

      # @api private
      def clear
        @mutex.synchronize { @regions = {} }
      end

      # @return [Hash] Returns a hash of cached bucket names and region names.
      def to_hash
        @mutex.synchronize do
          @regions.dup
        end
      end
      alias to_h to_hash

    end
  end
end
