# encoding: utf-8
module Origin

  # The options is a hash representation of options passed to MongoDB queries,
  # such as skip, limit, and sorting criteria.
  class Options < Smash

    # Convenience method for getting the field options.
    #
    # @example Get the fields options.
    #   options.fields
    #
    # @return [ Hash ] The fields options.
    #
    # @since 1.0.0
    def fields
      self[:fields]
    end

    # Convenience method for getting the limit option.
    #
    # @example Get the limit option.
    #   options.limit
    #
    # @return [ Integer ] The limit option.
    #
    # @since 1.0.0
    def limit
      self[:limit]
    end

    # Convenience method for getting the skip option.
    #
    # @example Get the skip option.
    #   options.skip
    #
    # @return [ Integer ] The skip option.
    #
    # @since 1.0.0
    def skip
      self[:skip]
    end

    # Convenience method for getting the sort options.
    #
    # @example Get the sort options.
    #   options.sort
    #
    # @return [ Hash ] The sort options.
    #
    # @since 1.0.0
    def sort
      self[:sort]
    end

    # Store the value in the options for the provided key. The options will
    # handle all necessary serialization and localization in this step.
    #
    # @example Store a value in the options.
    #   options.store(:key, "testing")
    #
    # @param [ String, Symbol ] key The name of the attribute.
    # @param [ Object ] value The value to add.
    #
    # @return [ Object ] The stored object.
    #
    # @since 1.0.0
    def store(key, value)
      super(key, evolve(value))
    end
    alias :[]= :store

    # Convert the options to aggregation pipeline friendly options.
    #
    # @example Convert the options to a pipeline.
    #   options.to_pipeline
    #
    # @return [ Array<Hash> ] The options in pipeline form.
    #
    # @since 2.0.0
    def to_pipeline
      pipeline = []
      pipeline.push({ "$skip" => skip }) if skip
      pipeline.push({ "$limit" => limit }) if limit
      pipeline.push({ "$sort" => sort }) if sort
      pipeline
    end

    # Perform a deep copy of the options.
    #
    # @example Perform a deep copy.
    #   options.__deep_copy__
    #
    # @return [ Options ] The copied options.
    #
    # @since 2.3.1
    def __deep_copy__
      self.class.new(aliases, serializers) do |copy|
        each_pair do |key, value|
          copy.merge!(key => value.__deep_copy__)
        end
      end
    end

    private

    # Evolve a single key selection with various types of values.
    #
    # @api private
    #
    # @example Evolve a simple selection.
    #   options.evolve(field, 5)
    #
    # @param [ Object ] value The value to serialize.
    #
    # @return [ Object ] The serialized object.
    #
    # @since 1.0.0
    def evolve(value)
      case value
      when Hash
        evolve_hash(value)
      else
        value
      end
    end

    # Evolve a single key selection with hash values.
    #
    # @api private
    #
    # @example Evolve a simple selection.
    #   options.evolve(field, { "$gt" => 5 })
    #
    # @param [ Hash ] value The hash to serialize.
    #
    # @return [ Object ] The serialized hash.
    #
    # @since 1.0.0
    def evolve_hash(value)
      value.inject({}) do |hash, (field, _value)|
        name, serializer = storage_pair(field)
        hash[normalized_key(name, serializer)] = _value
        hash
      end
    end
  end
end
