-- So that in addition to regex.gsub(pattern, string), we can also do
-- pattern:gsub(string).
regex.__index = function(table, key) return regex[key]; end

regex.match = function(pattern_string, string, offset, options)
  local pattern = type(pattern_string) == "table" and
    pattern_string or regex.compile(pattern_string)
  local s, e = regex.cmatch(pattern, string, offset or 1, options or 0)
  return s, e and e - 1
end

-- Will iterate back through any UTF-8 bytes so that we don't replace bits
-- mid character.
local function previous_character(str, index)
  local byte
  repeat
    index = index - 1
    byte = string.byte(str, index)
  until byte < 128 or byte >= 192
  return index
end

-- Moves to the end of the identified character.
local function end_character(str, index)
  local byte = string.byte(str, index + 1)
  while byte and byte >= 128 and byte < 192 do
    index = index + 1
    byte = string.byte(str, index + 1)
  end
  return index
end

-- Build off matching. For now, only support basic replacements, but capture
-- groupings should be doable. We can even have custom group replacements and
-- transformations and stuff in lua. Currently, this takes group replacements
-- as \1 - \9.
-- Should work on UTF-8 text.
regex.gsub = function(pattern_string, str, replacement)
  local pattern = type(pattern_string) == "table" and
    pattern_string or regex.compile(pattern_string)
  local result, indices = ""
  local matches, replacements = {}, {}
  repeat
    indices = { regex.cmatch(pattern, str) }
    if #indices > 0 then
      table.insert(matches, indices)
      local currentReplacement = replacement
      if #indices > 2 then
        for i = 1, (#indices/2 - 1) do
          currentReplacement = string.gsub(
            currentReplacement,
            "\\" .. i,
            str:sub(indices[i*2+1], end_character(str,indices[i*2+2]-1))
          )
        end
      end
      currentReplacement = string.gsub(currentReplacement, "\\%d", "")
      table.insert(replacements, { indices[1], #currentReplacement+indices[1] })
      if indices[1] > 1 then
        result = result ..
          str:sub(1, previous_character(str, indices[1])) .. currentReplacement
      else
        result = result .. currentReplacement
      end
      str = str:sub(indices[2])
    end
  until #indices == 0 or indices[1] == indices[2]
  return result .. str, matches, replacements
end