require 'uri'

module Parameters
  module Parser
    #
    # @return [Array]
    #   The parameter patterns and their parsers.
    #
    def Parser.formats
      @@parameters_parser_formats ||= []
    end

    #
    # Itereates over each parameter pattern and parser.
    #
    # @yield [pattern, parser]
    #   The block will be passed each parameter pattern and the associated
    #   parser.
    #
    # @yieldparam [Regexp, String] pattern
    #   The pattern to match recognize parameter values with.
    #
    # @yieldparam [Proc] parser
    #   The parser for the parameter value.
    #
    def Parser.each_format(&block)
      Parser.formats.each do |format|
        block.call(format[:pattern],format[:parser])
      end
    end

    #
    # Adds a new parameter parser.
    #
    # @param [Regexp, String] pattern
    #   The pattern to recognize parameter values with.
    #
    # @yield [value]
    #   The block will be used as the parser for the recognized parameter
    #   values.
    #
    # @yieldparam [String] value
    #   The parameter value to parser.
    #
    def Parser.recognize(pattern,&block)
      Parser.formats.unshift({
        :pattern => pattern,
        :parser => block
      })
    end

    #
    # Recognizes and parses the given parameter value.
    #
    # @param [String] value
    #   The parameter value to parse.
    #
    # @return [Object]
    #   The parsed parameter value.
    #
    # @example
    #   Parser.parse_value("0x1")
    #   # => 1
    #
    # @example
    #   Parser.parse_value("'mesg'")
    #   # => "mesg"
    #
    def Parser.parse_value(value)
      Parser.each_format do |pattern,parser|
        if value.match(pattern)
          return parser.call(value)
        end
      end

      return value
    end

    #
    # Parses the a parameter string of the form +name=value+.
    #
    # @param [String] name_and_value
    #   The name and value parameter join with a +=+ character.
    #
    # @return [Hash{Symbol => Object}]
    #   A singleton Hash containing the parameter name and it's value.
    #
    # @example
    #   Parser.parse_param('var=2')
    #   # => {:var=>2}
    #
    # @since 0.1.8
    #
    def Parser.parse_param(name_and_value)
      name, value = name_and_value.split('=',2)

      value = Parser.parse_value(value) if value
      return {name.to_sym => value}
    end

    #
    # Parses one or more parameter strings of the form +name=value+.
    #
    # @param [Array<String>] names_and_values
    #   The names and values of the parameters, joined by the +=+
    #   character.
    #
    # @return [Hash{Symbol => Object}]
    #   The names and values of the parameters.
    #
    # @example
    #   Parser.parse(["x=2", "y=true"])
    #   # => {:x=>2, :y=>true}
    #
    def Parser.parse(names_and_values)
      params = {}

      names_and_values.each do |name_and_value|
        params.merge!(Parser.parse_param(name_and_value))
      end

      return params
    end

    Parser.recognize(/^'(\\'|[^'])+'/) { |value|
      value[1...-1].gsub("\\'","'")
    }
    Parser.recognize(/^[a-zA-Z][a-zA-Z0-9]*:\/\//) { |value| URI(value) }
    Parser.recognize('false') { |value| false }
    Parser.recognize('true') { |value| true }
    Parser.recognize(/^[0-9]+$/) { |value| value.to_i }
    Parser.recognize(/^0[0-7]+$/) { |value| value.oct }
    Parser.recognize(/^0x[0-9a-fA-F]+$/) { |value| value.hex }

  end
end