-- #### START akamaiFunctions.lua

-- executed after all criteria and behavior are evaluated to apply final actions
function finalActions()

    ngx.var.aka_gzip = aka_gzip

    -- deal with an calculated access controls
    if ngx.var.aka_deny_reason ~= nil and ngx.var.aka_deny_reason ~= "" then
        ngx.var.aka_origin_host = ""
        ngx.header.content_type = "text/plain";
        ngx.status = ngx.HTTP_UNAUTHORIZED
        ngx.log(ngx.ERR, "access denied: " .. ngx.var.aka_deny_reason)
        ngx.say("access denied: " .. ngx.var.aka_deny_reason)
        ngx.exit(ngx.HTTP_UNAUTHORIZED)
    end

    -- deal with request method restrictions
    if aka_request_method_status[aka_request_method] == nil or aka_request_method_status[aka_request_method] ~= "ALLOW" then
        ngx.log(ngx.ERR, aka_request_method_status[aka_request_method])
        ngx.var.aka_origin_host = ""
        ngx.header.content_type = "text/plain";
        ngx.status = ngx.HTTP_NOT_ALLOWED
        ngx.log(ngx.ERR, "method not allowed: " .. aka_request_method)
        ngx.say("method not allowed: " .. aka_request_method)
        ngx.exit(ngx.HTTP_NOT_ALLOWED)
    end

    -- if redirect calculated, do it
    if ngx.var.aka_redirect_location ~= nil and ngx.var.aka_redirect_location ~= "" then
        ngx.log(ngx.ERR, "redirecting to: " .. ngx.var.aka_redirect_location .. " as " .. ngx.var.aka_redirect_code)
        ngx.redirect(ngx.var.aka_redirect_location, ngx.var.aka_redirect_code)
    end

    -- set upstream headers modified by behaviors
    for key,value in pairs(aka_upstream_headers) do
        ngx.req.set_header(key, value)
    end

    -- if we have not manipulated the path or qs, pass through to origin as is.
    if aka_origin_url == nil or aka_origin_url == "" then
        aka_origin_url = aka_request_path .. aka_request_qs
    end

    ngx.ctx["aka_downstream_headers"] = aka_downstream_headers

    ngx.var.aka_origin_url = aka_origin_url
    ngx.var.aka_origin_scheme = aka_request_scheme
    ngx.log(ngx.ERR, "origin request: " .. ngx.var.aka_origin_scheme .. "://" .. ngx.var.aka_origin_host .. ngx.var.aka_origin_url)

end

function mapValue(val)
    if val == nil then
        return ""
    end

    if valueMap[val] == nil then
        return val
    else
        return valueMap[val]
    end
end

ngx.ctx["cs"] = cs
ngx.ctx["mapValue"] = mapValue

function getVar(var)
    local fn = varMap[var]
    if fn ~= nil then
        return fn()
    end
end

function getVarNumber(var)
    local result
    local varString = getVar(var)
    if not tonumber(varString) then
        result = -1
    else
        result = tonumber(varString)
    end
    return result
end

function swapVars(value)
    local result = value
    for token in string.gmatch( value, "(%{%{[^%.]*%.[^%}]*%}%})" ) do
        -- ngx.log(ngx.ERR, token)
        local var = string.match( token, "%.([^%}]*)%}" )

        local replacement = getVar(var)

        if replacement ~= nil then
            ngx.log(ngx.ERR, "replacing var: " .. token .. " with [" .. replacement .. "]")
            result = result:gsub( "%{%{[^%.]*%." .. var .. "%}%}", replacement)
        else
            ngx.log(ngx.ERR, "removing unresolved var: " .. token)
            result = result:gsub( "%{%{[^%.]*%." .. var .. "%}%}", "")
        end
    end
    -- recurse if a var contained a var..
    if result:match("(%{%{[^%.]*%.[^%}]*%}%})" ) then
        result = swapVars(result)
    end
    return result;
end

function hostDomain(hostname)
    local parts = hostname:split(".")

    if #parts > 2 then
        local domain = ""
        for i = 2, #parts-1 do
            domain = domain .. parts[i] .. "."
        end
        domain = domain .. parts[#parts]
        return domain
    else
        return hostname
    end

end

function basePath(requestpath)
    local parts = requestpath:split("/")

    local result = ""
    for i = 2, #parts-1 do
        result = "/" .. parts[i]
    end
    return result

end

function trueClientIp()
    if ngx.header["X-Forwarded-For"] ~= nil then
        return ngx.header["X-Forwarded-For"]
    end
    if ngx.header["Akamai-Client-IP"] ~= nil then
        return ngx.header["Akamai-Client-IP"]
    end
    return ngx.var.remote_addr
end

function transformVariable(oldValue, options)

    local newValue
    local transformers = { }

    -- arithmetic
    transformers['NONE'] = function(val, opts) return val end
    transformers['ADD'] = function(val, opts) return tonumber(val) + tonumber(swapVars(opts.operandOne)) end
    transformers['SUBTRACT'] = function(val, opts) return tonumber(val) - tonumber(swapVars(opts.operandOne)) end
    transformers['MINUS'] = function(val, opts) return tonumber(val) * -1 end
    transformers['MULTIPLY'] = function(val, opts) return tonumber(val) * tonumber(swapVars(opts.operandOne)) end
    transformers['DIVIDE'] = function(val, opts) return tonumber(val) / tonumber(swapVars(opts.operandOne)) end
    transformers['MODULO'] = function(val, opts) return tonumber(val) % tonumber(swapVars(opts.operandOne)) end
    -- TODO: EXTRACT_PARAM + use "swapVars(paramName)", "separator"

    -- string
    transformers['LOWER'] = function(val, opts) return string.lower(val) end
    transformers['UPPER'] = function(val, opts) return string.upper(val) end
    transformers['STRING_LENGTH'] = function(val, opts) return string.len(val) end
    transformers['REMOVE_WHITESPACE'] = function(val, opts) return string.gsub(val, "%s+", "") end
    transformers['TRIM'] = function(val, opts) return string.gsub(val, "^%s*(.-)%s*$", "%1") end
    transformers['SUBSTITUTE'] = function(val, opts) return string.gsub(val, opts.regex, swapVars(opts.replacement)) end
        -- TODO+ use "regex", "replacement", "caseSensitive", "globalSubstitution"
        -- "replacement" supports group capture replacement via $1, $2 … $n

    transformers['SUBSTRING'] =
        function(val, opts)
            return string.sub(val, tonumber(swapVars(opts.startIndex)), tonumber(swapVars(opts.endIndex)))
        end

    transformers['STRING_INDEX'] =
        function(val, opts)
            local result = string.find(val, urldecode(swapVars(opts.subString)))
            if result == nil then
                result = -1
            end
            return result-1 -- to be zero based per akamai
        end

    -- encoding and decoding
    transformers['URL_ENCODE'] = function(val, opts) return urlencode(val) end -- TODO: exceptChars, forceChars
    transformers['URL_DECODE'] = function(val, opts) return urldecode(val) end
    transformers['HEX_ENCODE'] = function(val, opts) return string.tohex(val) end
    transformers['HEX_DECODE'] = function(val, opts) return string.fromhex(val) end
    -- TODO: XML_ENCODE, XML_DECODE, URL_DECODE_UNI, NORMALIZE_PATH_WIN

    -- bitwise operations
    -- TODO: BITWISE_AND, BITWISE_OR, BITWISE_XOR + use "operandOne" BITWISE_NOT

    -- numeric conversion
    transformers['DECIMAL_TO_HEX'] = function(val, opts) return decimalToHex(val) end
    transformers['HEX_TO_DECIMAL'] = function(val, opts) return hexToDecimal(val) end

    -- encoding/decoding
    -- TODO: DECRYPT, ENCRYPT
    transformers['BASE_64_ENCODE'] = function(val, opts) return base64encode(val) end
    transformers['BASE_64_DECODE'] = function(val, opts) return base64decode(val) end

    -- hash
    -- TODO: SHA_1, SHA_256, MD5, HMAC, HASH

    -- time formats
    transformers['UTC_SECONDS'] = function(val, opts) return os.time() end
    transformers['EPOCH_TO_STRING'] = function(val, opts) return os.date(opts.formatString, os.time(val)) end
    -- TODO: STRING_TO_EPOCH
    -- %m/%d/%y as specified by strftime

    -- networking
    -- TODO: NETMASK + use "ipVersion" (IPV4 or IPV6), "ipv6Prefix", "ipv4Prefix"


    local func = transformers[options["transform"]]
    if (func) then
        newValue = func(oldValue, options)
    else
        newValue = oldValue
    end

    return newValue
end

-- #### END akamaiFunctions.lua

