cString = {}

function cString.find_exact(s, pattern, delim, init, plain)
    local isNumeric = tonumber(pattern)
    if isNumeric then
        pattern = '%f[%d]'..pattern..'%f'..(delim or '[%D]')
    else
        pattern = '%f[%w]'..pattern..'%f'..(delim or '[%W]')
    end
    return s:find(pattern, init, plain)
end

function cString.as_bool(str)
    return str == "true" or str == "1"
end

function cString.as_vec2(str)
    local t = { 0, 0 }
    local i = 1
    for n in str:gmatch'[%d.-]+' do
        t[i] = tonumber(n)
        i = i+1
    end
    return t
end

function cString.as_vec(str)
    local t = { 0, 0, 0 }
    local i = 1
    for n in str:gmatch'[%d.-]+' do
        t[i] = tonumber(n)
        i = i+1
    end
    return t
end

function cString.as_transform(str)
    local t = str:as_vec()
    local pos = {t[1], t[2], t[3]}
    local rot = t[4] and {t[4], t[5], t[6], t[7]}
    return Transform(pos, rot)
end

function cString.as_values(str, pattern)
    local t = {}
    for n in str:gmatch(pattern or "%w+") do
        t[#t+1] = tonumber(n) or n:as_bool() or n
    end
    return t
end

function cString.as_keys(str, pattern)
    local t = {}
    for n in str:gmatch(pattern or "%w+") do
        t[n] = true
    end
    return t
end

function cString.as_map(str, pattern, value_proc_fn, tbl)
    tbl = tbl or {}
    for k, v in str:gmatch(pattern or "([^=,+]+)=([^=,+]+)") do
        if v == "" then k, v = v, k end
        if value_proc_fn then
            tbl[tonumber(k) or k:as_bool() or (k~="" and k) or #tbl+1] = value_proc_fn(v)
        else
            tbl[tonumber(k) or k:as_bool() or (k~="" and k) or #tbl+1] = tonumber(v) or v:as_bool() or v
        end
    end
    return tbl
end

function cString.count(str, substr)
    local count = 0
    local position = 1

    while true do
        position = str:find(substr, position, true)
        if position then
            count = count + 1
            position = position + 1
        else
            break
        end
    end

    return count
end

function cString.count_matches(str, pattern)
    local count = 0
    local position = 1
    local matching

    while true do
        matching = str:match(pattern, position)
        if matching then
            count = count + 1
            position = position + 1 + #matching
        else
            break
        end
    end

    return count
end

function cString.unpack_gmatch(str, pattern, i, j, c)
    i = i or 1
    j = j or math.huge
    c = c or 0
    if c >= j then
        return
    end
    for n in str:gmatch(pattern) do
        c = c + 1
        if c == i then
            local endpos = select(2, str:find(n, 0, true))
            if endpos then
                return n, str:sub(endpos + 2):unpack_gmatch(pattern, i+1, j, c)
            else
                return n
            end
        end
    end
end

string.find_exact = cString.find_exact
string.unpack_gmatch = cString.unpack_gmatch
string.as_bool = cString.as_bool
string.as_vec2 = cString.as_vec2
string.as_vec = cString.as_vec
string.as_transform = cString.as_transform
string.as_values = cString.as_values
string.as_keys = cString.as_keys
string.as_map = cString.as_map
string.count = cString.count
string.count_matches = cString.count_matches
