# Borrowed from: github.com/juretta/uri-templates/blob/master/grammar/uri_template.treetop

grammar UriTemplate

include DataMetaCommonsRoot

rule uri_template
  uri_element more_elements:(uri_element)* {
    def value(env={})
      uri_element.value(env) << more_elements.elements.map{|el| el.value(env)}.join
    end
  }
end

rule uri_element
  expansion / uri_part
end

rule expansion
  '{'
 c:(
    var
    /
    operator
 )
 '}'
  {
    def value(env = {})
      c.value(env)
    end
    }
end

rule uri_part
  (unreserved / reserved / pct_encoded) {
    def value(env = {})
      text_value
    end
  }
end

rule arg
  (reserved / unreserved / pct_encoded)*
end

rule op
  (
  'opt' {
              # If each variable is undefined or an empty list then substitute the
              # empty string, otherwise substitute the value of 'arg'.
       def exec
         lambda do |env, arg, vars|
           ret = ''
           vars.split(',').each do |var|
             if env[var] && (env[var].respond_to?(:length) ? env[var].length > 0 : true)
               ret = "#{arg}"
               break
             end
           end
           ret
         end
       end
     }
  /
  'neg' {
   # If all of the variables are un-defined or empty then substitute the
   # value of arg, otherwise substitute the empty string.
    def exec
      lambda do |env, arg, vars|
        ret = "#{arg}"
        vars.split(',').each do |var|
          if !env[var].to_s.blank?
            ret = ""
            break
          end
        end
        ret
      end
    end
  }
  /
  'prefix' {
   # The prefix operator MUST only have one variable in its expansion.  If
   # the variable is defined and non-empty then substitute the value of
   # arg followed by the value of the variable, otherwise substitute the
   # empty string.
    def exec
      lambda do |env, prefix, vars|
        v = env[vars]
        if vars =~ /([^=]+)=([^=]+)/
          var, default = $1.dup, $2.dup
          v = env[var]
          v = default if v.to_s.blank?
        end
        !v.blank? ? "#{prefix}#{UriTemplate::Encoder.encode(v)}" : ""
      end
    end
  }
  /
  'suffix' {
   # The suffix operator MUST only have one variable in its expansion.  If
   # the variable is defined and non-empty then substitute the value of
   # the variable followed by the value of arg, otherwise substitute the
   # empty string.
    def exec
      lambda do |env, append, vars|
        v = env[vars]
        if vars =~ /([^=]+)=([^=]+)/
          var, default = $1.dup, $2.dup
          v = env[var]
          v = default if v.to_s.blank?
        end
        if v
          val = UriTemplate::Encoder.encode(v)
          !val.blank? ? "#{val}#{append}" : ""
        else
          ''
        end
      end
    end
  }
  /
  'join' {
   # For each variable that is defined and non-empty create a keyvalue
   # string that is the concatenation of the variable name, "=", and the
   # variable value.  Concatenate more than one keyvalue string with
   # intervening values of arg to create the substitution value.
   def exec
      lambda do |env, joinop, vars|
        vars.split(',').map do |var|
        v = env[var]
        if var =~ /([^=]+)=([^=]+)/
          var, default = $1.dup, $2.dup
          v = env[var]
          v = default if v.to_s.blank?
        end
        "#{var}=#{UriTemplate::Encoder.encode(v)}" if v
      end.compact.join(joinop)
    end
   end
 }
  /
  'list' {
  #   The listjoin operator MUST have only one variable in its expansion
  # and that variable must be a list.  More than one variable is an
  # error.  If the list is non-empty then substitute the concatenation of
  # all the list members with intervening values of arg.  If the list is
  # empty or the variable is undefined them substitute the empty string.
   def exec
     lambda do |env, joinop, vars|
       return "" unless env[vars].respond_to? :each
       env[vars].map do |v|
         "#{UriTemplate::Encoder.encode(v)}" if v
       end.compact.join(joinop)
     end
   end
 }
  )
end

rule vars
  var ("," var)*
end

rule vardefault
  (unreserved / pct_encoded)*
end

rule var
  varname defaults:('=' vardefault)* {
    def value(env={} )
      return UriTemplate::Encoder.encode(env[name]) unless env[name].nil?
      defaults.text_value.gsub(/=/, '')
    end

    def name
      varname.text_value
    end
  }
end

rule operator
  "-" op "|" arg "|" vars {
    def value(env={})
      op.exec.call(env, arg.text_value, vars.text_value) # if op.respond_to?(:exec)
    end
  }
end

rule varname
  [a-zA-Z0-9] [a-zA-Z0-9_.-]*
end

rule alpha
  [A-Za-z_]
end

rule alphanumeric
  alpha / [0-9]
end

# see http://www.ietf.org/rfc/rfc3986.txt
rule unreserved
  alphanumeric / "-" / "." / "_" / "~"
end

# see http://www.ietf.org/rfc/rfc3986.txt
rule pct_encoded
  '%' hexdig hexdig
end

rule hexdig
  [a-fA-F0-9]
end

# see http://www.ietf.org/rfc/rfc3986.txt
rule reserved
  gen_delims / sub_delims
end

rule gen_delims
  ":" / "/" / "?" / "#" / "[" / "]" / "@"
end

rule sub_delims
  "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
end

end