local M = { _NAME = "uri._util" }

local string_char, string_format = string.char, string.format

-- Build a char->hex map
local escapes = {}
for i = 0, 255 do
    escapes[string_char(i)] = string_format("%%%02X", i)
end
local function _encode_char (chr) return escapes[chr] end

function M.uri_encode (text, patn)
    if not text then return end
    if not patn then
        -- Default unsafe characters.  RFC 2732 ^(uric - reserved)
        -- TODO - this should be updated to the latest RFC.
        patn = "^A-Za-z0-9%-_.!~*'()"
    end
    return (text:gsub("([" .. patn .. "])", _encode_char))
end

function M.uri_decode (str, patn)
    -- Note from RFC1630:  "Sequences which start with a percent sign
    -- but are not followed by two hexadecimal characters are reserved
    -- for future extension"
    if not str then return end
    if patn then patn = "[" .. patn .. "]" end
    return (str:gsub("%%(%x%x)", function (hex)
        local char = string_char(tonumber(hex, 16))
        return (patn and not char:find(patn)) and "%" .. hex or char
    end))
end

-- This is the remove_dot_segments algorithm from RFC 3986 section 5.2.4.
-- The input buffer is 's', the output buffer 'path'.
function M.remove_dot_segments (s)
    local path = ""

    while s ~= "" do
        if s:find("^%.%.?/") then                       -- A
            s = s:gsub("^%.%.?/", "", 1)
        elseif s:find("^/%./") or s == "/." then        -- B
            s = s:gsub("^/%./?", "/", 1)
        elseif s:find("^/%.%./") or s == "/.." then     -- C
            s = s:gsub("^/%.%./?", "/", 1)
            if path:find("/") then
                path = path:gsub("/[^/]*$", "", 1)
            else
                path = ""
            end
        elseif s == "." or s == ".." then               -- D
            s = ""
        else                                            -- E
            local _, p, seg = s:find("^(/?[^/]*)")
            s = s:sub(p + 1)
            path = path .. seg
        end
    end

    return path
end

-- TODO - wouldn't this be better as a method on string?  s:split(patn)
function M.split (patn, s, max)
    if s == "" then return {} end

    local i, j = 1, string.find(s, patn)
    if not j then return { s } end

    local list = {}
    while true do
        if #list + 1 == max then list[max] = s:sub(i); return list end
        list[#list + 1] = s:sub(i, j - 1)
        i = j + 1
        j = string.find(s, patn, i)
        if not j then
            list[#list + 1] = s:sub(i)
            break
        end
    end
    return list
end

function M.attempt_require (modname)
    local ok, result = pcall(require, modname)
    if ok then
        return result
    elseif type(result) == "string" and
           result:find("module '.*' not found") then
        return nil
    else
        error(result)
    end
end

function M.subclass_of (class, baseclass)
    class.__index = class
    class.__tostring = baseclass.__tostring
    class._SUPER = baseclass
    setmetatable(class, baseclass)
end

function M.do_class_changing_change (uri, baseclass, changedesc, newvalue,
                                     changefunc)
    local tmpuri = {}
    setmetatable(tmpuri, baseclass)
    for k, v in pairs(uri) do tmpuri[k] = v end
    changefunc(tmpuri, newvalue)
    tmpuri._uri = nil

    local foo, err = tmpuri:init()
    if not foo then
        error("URI not valid after " .. changedesc .. " changed to '" ..
              newvalue .. "': " .. err, 3)
    end

    setmetatable(uri, getmetatable(tmpuri))
    for k in pairs(uri) do uri[k] = nil end
    for k, v in pairs(tmpuri) do uri[k] = v end
end

function M.uri_part_not_allowed (class, method)
    class[method] = function (self, new)
        if new then error(method .. " not allowed on this kind of URI") end
        return self["_" .. method]
    end
end

return M
-- vi:ts=4 sw=4 expandtab
