# Copyright 2010-present Basho Technologies, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Riak
  class Client
    # Methods that can be used to determine whether certain features
    # are supported by the Riak node to which the client backend is
    # connected.
    #
    # Backends must implement the "get_server_version" method,
    # returning a string representing the Riak node's version. This is
    # implemented on HTTP using the stats resource, and on Protocol
    # Buffers using the RpbGetServerInfoReq message.
    module FeatureDetection
      # Constants representing Riak versions
      VERSION = {
        1 => Gem::Version.new("1.0.0"),
        1.1 => Gem::Version.new("1.1.0"),
        1.2 => Gem::Version.new("1.2.0"),
        1.3 => Gem::Version.new("1.3.0"),
        1.4 => Gem::Version.new("1.4.0")
      }.freeze

      # @return [String] the version of the Riak node
      # @abstract
      def get_server_version
        raise NotImplementedError
      end

      # @return [Gem::Version] the version of the Riak node to which
      #   this backend is connected
      def server_version
        @server_version ||= Gem::Version.new(get_server_version.split("-").first)
      end

      # @return [true,false] whether MapReduce requests can be submitted without
      #   phases.
      def mapred_phaseless?
        at_least? VERSION[1.1]
      end

      # @return [true,false] whether secondary index queries are
      #  supported over Protocol Buffers
      def pb_indexes?
        at_least? VERSION[1.2]
      end

      # @return [true,false] whether search queries are supported over
      #   Protocol Buffers
      def pb_search?
        at_least? VERSION[1.2]
      end

      # @return [true,false] whether conditional fetch/store semantics
      #   are supported over Protocol Buffers
      def pb_conditionals?
        at_least? VERSION[1]
      end

      # @return [true,false] whether additional quorums and FSM
      #   controls are available, e.g. primary quorums, basic_quorum,
      #   notfound_ok
      def quorum_controls?
        at_least? VERSION[1]
      end

      # @return [true,false] whether "not found" responses might
      #   include vclocks
      def tombstone_vclocks?
        at_least? VERSION[1]
      end

      # @return [true,false] whether partial-fetches (vclock and
      #   metadata only) are supported over Protocol Buffers
      def pb_head?
        at_least? VERSION[1]
      end

      # @return [true,false] whether bucket properties can be cleared
      #   (reset to defaults) over HTTP
      def http_props_clearable?
        at_least? VERSION[1.3]
      end

      # @return [true,false] whether secondary indexes support
      #   pagination
      def index_pagination?
        at_least? VERSION[1.4]
      end

      # @return [true,false] whether secondary indexes support
      #   return_terms
      def index_return_terms?
        at_least? VERSION[1.4]
      end

      # @return [true,false] whether secondary indexes support
      #   streaming
      def index_streaming?
        at_least? VERSION[1.4]
      end

      # @return [true,false] whether timeouts are accepted for
      #   object CRUD, key listing, and bucket listing
      def key_object_bucket_timeouts?
        at_least? VERSION[1.4]
      end

      protected
      # @return [true,false] whether the server version is the same or
      #  newer than the requested version
      def at_least?(version)
        server_version >= version
      end

      # Backends should call this when their connection is interrupted
      # or reset so as to facilitate rolling upgrades
      def reset_server_version
        @server_version = nil
      end
    end
  end
end
