# -*- encoding: utf-8 -*-

require 'data_mapper/validation/rule'

module DataMapper
  module Validation
    class Rule

      module Length

        attr_reader :expected

        # TODO: DRY this up (also implemented in Rule)
        def self.rules_for(attribute_name, options)
          Array(new(attribute_name, options))
        end

        # TODO: move options normalization into the validator macros
        def self.new(attribute_name, options)
          options = options.dup

          equal   = options.delete(:is)      || options.delete(:equals)
          range   = options.delete(:within)  || options.delete(:in)
          minimum = options.delete(:minimum) || options.delete(:min)
          maximum = options.delete(:maximum) || options.delete(:max)

          if minimum && maximum
            range ||= minimum..maximum
          end

          if equal
            Length::Equal.new(attribute_name,   options.merge(:equal => equal))
          elsif range
            Length::Range.new(attribute_name,   options.merge(:range => range))
          elsif minimum
            Length::Minimum.new(attribute_name, options.merge(:minimum => minimum))
          elsif maximum
            Length::Maximum.new(attribute_name, options.merge(:maximum => maximum))
          else
            # raise ArgumentError, "expected one of :is, :equals, :within, :in, :minimum, :min, :maximum, or :max; got #{options.keys.inspect}"
            warn "expected length specification: one of :is, :equals, :within, :in, :minimum, :min, :maximum, or :max; got #{options.keys.inspect}"
            Length::Dummy.new(attribute_name, options)
          end
        end

        class Dummy < Rule
          include Length
        end

        def valid?(resource)
          value = resource.validation_property_value(attribute_name)

          if optional?(value)
            true
          else
            length = value_length(value.to_s)
            valid_length?(length)
          end
        end

      private

        def valid_length?(length)
          raise NotImplementedError, "#{self.class}#valid_length? must be implemented"
        end

        # Return the length in characters
        #
        # @param [#to_str] value
        #   the string to get the number of characters for
        #
        # @return [Integer]
        #   the number of characters in the string
        #
        # @api private
        def value_length(value)
          value.to_str.length
        end

        if RUBY_VERSION < '1.9'
          def value_length(value)
            value.to_str.scan(/./u).size
          end
        end

      end # module Length

    end # class Rule
  end # module Validation
end # module DataMapper

# meh, I don't like doing this, but the superclass must be loaded before subclasses
require 'data_mapper/validation/rule/length/equal'
require 'data_mapper/validation/rule/length/range'
require 'data_mapper/validation/rule/length/minimum'
require 'data_mapper/validation/rule/length/maximum'
