-- read namespace from global env
local _G = _G
local Grichelde = _G.Grichelde

-- constants and upvalues
Grichelde.LOG_LEVEL = {}
Grichelde.LOG_LEVEL.DEBUG = 1
Grichelde.LOG_LEVEL.TRACE = 2

Grichelde.MAPPING_OFFSET = 10

Grichelde.ICONS = {}
Grichelde.ICONS.MOVE_UP = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Up"
Grichelde.ICONS.MOVE_DOWN = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Up"

-- colors:
Grichelde.COLORS = {}
Grichelde.COLORS.NORMAL    = _G.NORMAL_FONT_COLOR
Grichelde.COLORS.HIGHLIGHT = _G.HIGHLIGHT_FONT_COLOR
Grichelde.COLORS.RED       = _G.RED_FONT_COLOR
Grichelde.COLORS.GREEN     = _G.GREEN_FONT_COLOR

Grichelde.COLOR_CODES = {}
Grichelde.COLOR_CODES.PREFIX      = "|c00FFAA00"
-- https://github.com/stoneharry/Misc-WoW-Stuff/blob/master/EoC%20Interface/FrameXML/Constants.lua
Grichelde.COLOR_CODES.NORMAL      = _G.NORMAL_FONT_COLOR_CODE      or "|cffffd200"
Grichelde.COLOR_CODES.HIGHLIGHT	  = _G.HIGHLIGHT_FONT_COLOR_CODE   or "|cffffffff"
Grichelde.COLOR_CODES.RED         = _G.RED_FONT_COLOR_CODE         or "|cffff2020"
Grichelde.COLOR_CODES.GREEN       = _G.GREEN_FONT_COLOR_CODE       or "|cff20ff20"
Grichelde.COLOR_CODES.LIGHTGRAY   = "|cffC0C0C0"
Grichelde.COLOR_CODES.GRAY        = _G.GRAY_FONT_COLOR_CODE        or "|cff808080"
Grichelde.COLOR_CODES.DARKGRAY    = "|cff404040"
Grichelde.COLOR_CODES.YELLOW      = _G.YELLOW_FONT_COLOR_CODE      or "|cffffff00"
Grichelde.COLOR_CODES.LIGHTYELLOW = _G.LIGHTYELLOW_FONT_COLOR_CODE or "|cffffff9a"
Grichelde.COLOR_CODES.ORANGE      = _G.ORANGE_FONT_COLOR_CODE      or "|cffff7f3f"
Grichelde.COLOR_CODES.CLOSE       = _G.FONT_COLOR_CODE_CLOSE       or "|r"

Grichelde.slashCommands = { "/s", "/say", "/e", "/em", "/me", "/emote", "/y", "/yell", "/sh", "/shout", "/p", "/party", "/pl", "/partyleader", "/g", "/gc", "/guild", "/o", "/osay", "/officer", "/raid", "/rsay", "/rl", "/raidleader", "/rw", "/raidwarning", "/i", "/instance", "/bg", "/battleground", "/w", "/whisper", "/t", "/tell", "/send", "/r", "/reply" }

local function nilOrEmpty(s)
    return s == nil or s:trim() == ""
end

local function spairs(t, orderFunc)
    -- collect the keys
    local sortedKeys = {}
    -- for every non-nil value
    for key, _ in Grichelde.functions.pairs(t) do
        Grichelde.functions.tInsert(sortedKeys, key)
    end

    if orderFunc then
        Grichelde.functions.tSort(sortedKeys, function(a, b) return orderFunc(sortedKeys, a, b) end)
    else
        -- lexicographical order
        Grichelde.functions.tSort(sortedKeys)
    end

    -- return the iterator function
    local it = 0
    return function()
        it = it + 1
        if sortedKeys[it] then
            return sortedKeys[it], t[sortedKeys[it]]
        else
            return nil
        end
    end
end

local function tFilter(t, cond, extr)
    local filtered = {}
    for key, value in Grichelde.functions.pairs(t) do
        if cond(key, value) then
            local val = extr(key, value)
            Grichelde.functions.tInsert(filtered, #filtered + 1, val)
        end
    end
    return filtered
end

local function tSize(t)
    local size = 0
    if (t) then
        -- for every non-nil value
        for _, _ in Grichelde.functions.pairs(t) do
            size = size + 1
        end
    end
    return size
end

local function tClone(orig)
    local orig_type = Grichelde.functions.type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        -- for every non-nil value
        for orig_key, orig_value in Grichelde.functions.tNext, orig, nil do
            copy[tClone(orig_key)] = tClone(orig_value)
        end
        Grichelde.functions.setmetatable(copy, tClone(Grichelde.functions.getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

local function isChar(word)
    return Grichelde.functions.find(word, "%a+")
end

local function isNumber(digit)
    return Grichelde.functions.find(digit, "%d+")
end

local function isUpper(word)
    return Grichelde.functions.isChar(word) and word == Grichelde.functions.toUpper(word)
end

local function isLower(word)
    return Grichelde.functions.isChar(word) and word == Grichelde.functions.toLower(word)
end

local function isCapital(word)
    return Grichelde.functions.legnth(word) > 1
        and Grichelde.functions.isUpper(Grichelde.functions.sub(word,1,1))
        and Grichelde.functions.isLower(Grichelde.functions.sub(word,2))
end

-- faster function lookups by mapping to local refs
Grichelde.functions = {}
Grichelde.functions.IsAddOnLoaded = _G.IsAddOnLoaded
Grichelde.functions.type          = _G.type
Grichelde.functions.print         = _G.print
Grichelde.functions.nilOrEmpty    = nilOrEmpty
Grichelde.functions.pairs         = _G.pairs
Grichelde.functions.ipairs        = _G.ipairs
Grichelde.functions.spairs        = spairs
Grichelde.functions.tContains     = _G.tContains
Grichelde.functions.tFilter       = tFilter
Grichelde.functions.tInsert       = _G.table.insert
Grichelde.functions.tConcat       = _G.table.concat
Grichelde.functions.tSize         = tSize
Grichelde.functions.tSort         = _G.table.sort
Grichelde.functions.tClone        = tClone
Grichelde.functions.tNext         = _G.next
Grichelde.functions.setmetatable  = _G.setmetatable
Grichelde.functions.getmetatable  = _G.getmetatable
Grichelde.functions.select        = _G.select
Grichelde.functions.unpack        = _G.unpack
Grichelde.functions.find          = _G.string.find
Grichelde.functions.sub           = _G.string.sub
Grichelde.functions.gsub          = _G.string.gsub
Grichelde.functions.match         = _G.strmatch
Grichelde.functions.join          = _G.strjoin
Grichelde.functions.split         = _G.strsplit
Grichelde.functions.toUpper       = _G.strupper
Grichelde.functions.toLower       = _G.strlower
Grichelde.functions.isChar        = isChar
Grichelde.functions.isNumber      = isNumber
Grichelde.functions.isUpper       = isUpper
Grichelde.functions.isLower       = isLower
Grichelde.functions.isCapital     = isCapital
Grichelde.functions.format        = _G.string.format
Grichelde.functions.rep           = _G.string.rep
Grichelde.functions.trim          = _G.strtrim
Grichelde.functions.length        = _G.string.len
Grichelde.functions.toString      = _G.tostring
Grichelde.functions.toNumber      = _G.tonumber
Grichelde.functions.max           = _G.math.max
Grichelde.functions.min           = _G.math.min