---@diagnostic disable: inject-field, duplicate-set-field, unbalanced-assignments
--@curseforge-project-slug: libsharedmedia-3-0@
--[[
Name: LibSharedMedia-3.0
Revision: $Revision: 164 $
Author: Elkano (elkano@gmx.de)
Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com)
Website: https://www.curseforge.com/wow/addons/libsharedmedia-3-0
Description: Shared handling of media data (fonts, sounds, textures, ...) between addons.
Dependencies: LibStub, CallbackHandler-1.0
License: LGPL v2.1
]]

local MAJOR, MINOR = "LibSharedMedia-3.0", 12000001 -- 12.0.0 v1 / increase manually on changes
local lib = LibStub:NewLibrary(MAJOR, MINOR)

if not lib then return end

local _G = getfenv(0)

local pairs         = _G.pairs
local type          = _G.type

local band          = _G.bit.band
local math_floor    = _G.math.floor
local table_insert  = _G.table.insert
local table_sort    = _G.table.sort

local locale = GetLocale()
local locale_is_western
local LOCALE_MASK
lib.LOCALE_BIT_koKR     = 1
lib.LOCALE_BIT_ruRU     = 2
lib.LOCALE_BIT_zhCN     = 4
lib.LOCALE_BIT_zhTW     = 8
lib.LOCALE_BIT_western  = 128

local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")

lib.callbacks       = lib.callbacks     or CallbackHandler:New(lib)

lib.DefaultMedia    = lib.DefaultMedia  or {}
lib.MediaList       = lib.MediaList     or {}
lib.MediaTable      = lib.MediaTable    or {}
lib.MediaType       = lib.MediaType     or {}
lib.OverrideMedia   = lib.OverrideMedia or {}

local defaultMedia = lib.DefaultMedia
local mediaList = lib.MediaList
local mediaTable = lib.MediaTable
local overrideMedia = lib.OverrideMedia


-- create mediatype constants
lib.MediaType.BACKGROUND    = "background"  -- background textures
lib.MediaType.BORDER        = "border"      -- border textures
lib.MediaType.FONT          = "font"        -- fonts
lib.MediaType.STATUSBAR     = "statusbar"   -- statusbar textures
lib.MediaType.SOUND         = "sound"       -- sound files

-- populate lib with default Blizzard data
-- BACKGROUND
if not lib.MediaTable.background then lib.MediaTable.background = {} end
lib.MediaTable.background["None"]                               = [[]]
lib.MediaTable.background["Blizzard Collections Background"]    = [[Interface\Collections\CollectionsBackgroundTile]]
lib.MediaTable.background["Blizzard Dialog Background"]         = [[Interface\DialogFrame\UI-DialogBox-Background]]
lib.MediaTable.background["Blizzard Dialog Background Dark"]    = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]]
lib.MediaTable.background["Blizzard Dialog Background Gold"]    = [[Interface\DialogFrame\UI-DialogBox-Gold-Background]]
lib.MediaTable.background["Blizzard Garrison Background"]       = [[Interface\Garrison\GarrisonUIBackground]]
lib.MediaTable.background["Blizzard Garrison Background 2"]     = [[Interface\Garrison\GarrisonUIBackground2]]
lib.MediaTable.background["Blizzard Garrison Background 3"]     = [[Interface\Garrison\GarrisonMissionUIInfoBoxBackgroundTile]]
lib.MediaTable.background["Blizzard Low Health"]                = [[Interface\FullScreenTextures\LowHealth]]
lib.MediaTable.background["Blizzard Marble"]                    = [[Interface\FrameGeneral\UI-Background-Marble]]
lib.MediaTable.background["Blizzard Out of Control"]            = [[Interface\FullScreenTextures\OutOfControl]]
lib.MediaTable.background["Blizzard Parchment"]                 = [[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]]
lib.MediaTable.background["Blizzard Parchment 2"]               = [[Interface\AchievementFrame\UI-GuildAchievement-Parchment-Horizontal]]
lib.MediaTable.background["Blizzard Rock"]                      = [[Interface\FrameGeneral\UI-Background-Rock]]
lib.MediaTable.background["Blizzard Tabard Background"]         = [[Interface\TabardFrame\TabardFrameBackground]]
lib.MediaTable.background["Blizzard Tooltip"]                   = [[Interface\Tooltips\UI-Tooltip-Background]]
lib.MediaTable.background["Solid"]                              = [[Interface\Buttons\WHITE8X8]]
lib.DefaultMedia.background = "None"

-- BORDER
if not lib.MediaTable.border then lib.MediaTable.border = {} end
lib.MediaTable.border["None"]                       = [[]]
lib.MediaTable.border["Blizzard Achievement Wood"]  = [[Interface\AchievementFrame\UI-Achievement-WoodBorder]]
lib.MediaTable.border["Blizzard Chat Bubble"]       = [[Interface\Tooltips\ChatBubble-Backdrop]]
lib.MediaTable.border["Blizzard Dialog"]            = [[Interface\DialogFrame\UI-DialogBox-Border]]
lib.MediaTable.border["Blizzard Dialog Gold"]       = [[Interface\DialogFrame\UI-DialogBox-Gold-Border]]
lib.MediaTable.border["Blizzard Party"]             = [[Interface\CHARACTERFRAME\UI-Party-Border]]
lib.MediaTable.border["Blizzard Tooltip"]           = [[Interface\Tooltips\UI-Tooltip-Border]]
lib.DefaultMedia.border = "None"

-- FONT
if not lib.MediaTable.font then lib.MediaTable.font = {} end
local SML_MT_font = lib.MediaTable.font
--[[
All font files are currently in all clients, the following table depicts which font supports which charset as of 5.0.4
Fonts were checked using langcover.pl from DejaVu fonts (http://sourceforge.net/projects/dejavu/) and FontForge (http://fontforge.org/)
latin means check for: de, en, es, fr, it, pt

file                name                            latin   koKR    ruRU    zhCN    zhTW
2002.ttf            2002                            X       X       X       -       -
2002B.ttf           2002 Bold                       X       X       X       -       -
ARHei.ttf           AR CrystalzcuheiGBK Demibold    X       -       X       X       X
ARIALN.TTF          Arial Narrow                    X       -       X       -       -
ARKai_C.ttf         AR ZhongkaiGBK Medium (Combat)  X       -       X       X       X
ARKai_T.ttf         AR ZhongkaiGBK Medium           X       -       X       X       X
bHEI00M.ttf         AR Heiti2 Medium B5             -       -       -       -       X
bHEI01B.ttf         AR Heiti2 Bold B5               -       -       -       -       X
bKAI00M.ttf         AR Kaiti Medium B5              -       -       -       -       X
bLEI00D.ttf         AR Leisu Demi B5                -       -       -       -       X
FRIZQT__.TTF        Friz Quadrata TT                X       -       -       -       -
FRIZQT___CYR.TTF    FrizQuadrataCTT                 x       -       X       -       -
K_Damage.TTF        YDIWingsM                       -       X       X       -       -
K_Pagetext.TTF      MoK                             X       X       X       -       -
MORPHEUS.TTF        Morpheus                        X       -       -       -       -
MORPHEUS_CYR.TTF    Morpheus                        X       -       X       -       -
NIM_____.ttf        Nimrod MT                       X       -       X       -       -
SKURRI.TTF          Skurri                          X       -       -       -       -
SKURRI_CYR.TTF      Skurri                          X       -       X       -       -

WARNING: Although FRIZQT___CYR is available on western clients, it doesn't support special European characters e.g. é, ï, ö
Due to this, we cannot use it as a replacement for FRIZQT__.TTF
]]

if locale == "koKR" then
    LOCALE_MASK = lib.LOCALE_BIT_koKR
--
    SML_MT_font["굵은 글꼴"]    = [[Fonts\2002B.TTF]]
    SML_MT_font["기본 글꼴"]    = [[Fonts\2002.TTF]]
    SML_MT_font["데미지 글꼴"]  = [[Fonts\K_Damage.TTF]]
    SML_MT_font["퀘스트 글꼴"]  = [[Fonts\K_Pagetext.TTF]]
--
    lib.DefaultMedia["font"] = "기본 글꼴" -- someone from koKR please adjust if needed
--
elseif locale == "zhCN" then
    LOCALE_MASK = lib.LOCALE_BIT_zhCN
--
    SML_MT_font["伤害数字"] = [[Fonts\ARKai_C.ttf]]
    SML_MT_font["默认"]     = [[Fonts\ARKai_T.ttf]]
    SML_MT_font["聊天"]     = [[Fonts\ARHei.ttf]]
--
    lib.DefaultMedia["font"] = "默认" -- someone from zhCN please adjust if needed
--
elseif locale == "zhTW" then
    LOCALE_MASK = lib.LOCALE_BIT_zhTW
--
    SML_MT_font["提示訊息"] = [[Fonts\bHEI00M.ttf]]
    SML_MT_font["聊天"]     = [[Fonts\bHEI01B.ttf]]
    SML_MT_font["傷害數字"] = [[Fonts\bKAI00M.ttf]]
    SML_MT_font["預設"]     = [[Fonts\bLEI00D.ttf]]
--
    lib.DefaultMedia["font"] = "預設" -- someone from zhTW please adjust if needed

elseif locale == "ruRU" then
    LOCALE_MASK = lib.LOCALE_BIT_ruRU
--
    SML_MT_font["2002"]                             = [[Fonts\2002.TTF]]
    SML_MT_font["2002 Bold"]                        = [[Fonts\2002B.TTF]]
    SML_MT_font["AR CrystalzcuheiGBK Demibold"]     = [[Fonts\ARHei.TTF]]
    SML_MT_font["AR ZhongkaiGBK Medium (Combat)"]   = [[Fonts\ARKai_C.TTF]]
    SML_MT_font["AR ZhongkaiGBK Medium"]            = [[Fonts\ARKai_T.TTF]]
    SML_MT_font["Arial Narrow"]                     = [[Fonts\ARIALN.TTF]]
    SML_MT_font["Friz Quadrata TT"]                 = [[Fonts\FRIZQT___CYR.TTF]]
    SML_MT_font["MoK"]                              = [[Fonts\K_Pagetext.TTF]]
    SML_MT_font["Morpheus"]                         = [[Fonts\MORPHEUS_CYR.TTF]]
    SML_MT_font["Nimrod MT"]                        = [[Fonts\NIM_____.ttf]]
    SML_MT_font["Skurri"]                           = [[Fonts\SKURRI_CYR.TTF]]
--
    lib.DefaultMedia.font = "Friz Quadrata TT"
--
else
    LOCALE_MASK = lib.LOCALE_BIT_western
    locale_is_western = true
--
    SML_MT_font["2002"]                             = [[Fonts\2002.TTF]]
    SML_MT_font["2002 Bold"]                        = [[Fonts\2002B.TTF]]
    SML_MT_font["AR CrystalzcuheiGBK Demibold"]     = [[Fonts\ARHei.TTF]]
    SML_MT_font["AR ZhongkaiGBK Medium (Combat)"]   = [[Fonts\ARKai_C.TTF]]
    SML_MT_font["AR ZhongkaiGBK Medium"]            = [[Fonts\ARKai_T.TTF]]
    SML_MT_font["Arial Narrow"]                     = [[Fonts\ARIALN.TTF]]
    SML_MT_font["Friz Quadrata TT"]                 = [[Fonts\FRIZQT__.TTF]]
    SML_MT_font["MoK"]                              = [[Fonts\K_Pagetext.TTF]]
    SML_MT_font["Morpheus"]                         = [[Fonts\MORPHEUS_CYR.TTF]]
    SML_MT_font["Nimrod MT"]                        = [[Fonts\NIM_____.ttf]]
    SML_MT_font["Skurri"]                           = [[Fonts\SKURRI_CYR.TTF]]
--
    lib.DefaultMedia.font = "Friz Quadrata TT"
--
end

-- STATUSBAR
if not lib.MediaTable.statusbar then lib.MediaTable.statusbar = {} end
lib.MediaTable.statusbar["Blizzard"]                        = [[Interface\TargetingFrame\UI-StatusBar]]
lib.MediaTable.statusbar["Blizzard Character Skills Bar"]   = [[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]]
lib.MediaTable.statusbar["Blizzard Raid Bar"]               = [[Interface\RaidFrame\Raid-Bar-Hp-Fill]]
lib.MediaTable.statusbar["Solid"]                           = [[Interface\Buttons\WHITE8X8]]
lib.DefaultMedia.statusbar = "Blizzard"

-- SOUND
if not lib.MediaTable.sound then lib.MediaTable.sound = {} end
lib.MediaTable.sound["None"] = 1 -- Relies on the fact that PlaySoundFile doesn't error on this value
lib.DefaultMedia.sound = "None"

local function buildMediaList(mediatype)
    local mtable = mediaTable[mediatype]
    if not mtable then return end
    if not mediaList[mediatype] then mediaList[mediatype] = {} end
    local mlist = mediaList[mediatype]
    -- list can only get larger, so simply overwrite it (should be empty anyways)
    local i = 0
    for k in pairs(mtable) do
        i = i + 1
        mlist[i] = k
    end
    table_sort(mlist)
end

local function updateMediaList(mediatype, value)
    if not mediaList[mediatype] then
        -- nothing to update, yet
        return
    end

    local mlist = mediaList[mediatype]
    -- invariant: list is sorted, entries are unique, value not yet in list
    local s, e, m = 1, #mlist
    while s <= e do
        m = math_floor((s + e) / 2)
        if mlist[m] > value then
            e = m - 1
        else
            s = m + 1
        end
    end
    table_insert(mlist, s, value)
end

function lib:Register(mediatype, key, data, langmask)
    if type(mediatype) ~= "string" then
        error(MAJOR..":Register(mediatype, key, data, langmask) - mediatype must be string, got "..type(mediatype))
    end
    if type(key) ~= "string" then
        error(MAJOR..":Register(mediatype, key, data, langmask) - key must be string, got "..type(key))
    end
    mediatype = mediatype:lower()
    if mediatype == lib.MediaType.FONT and ((langmask and band(langmask, LOCALE_MASK) == 0) or not (langmask or locale_is_western)) then
        -- ignore fonts that aren't flagged as supporting local glyphs on non-western clients
        return false
    end
    if type(data) == "string" and (mediatype == lib.MediaType.BACKGROUND or mediatype == lib.MediaType.BORDER or mediatype == lib.MediaType.STATUSBAR or mediatype == lib.MediaType.SOUND) then
        local path = data:lower()
        if not path:find("^interface") then
            -- files accessed via path only allowed from interface folder
            return false
        end
        if mediatype == lib.MediaType.SOUND and not (path:find(".ogg", nil, true) or path:find(".mp3", nil, true)) then
            -- only ogg and mp3 are valid sounds
            return false
        end
    end
    if not mediaTable[mediatype] then mediaTable[mediatype] = {} end
    local mtable = mediaTable[mediatype]
    if mtable[key] then
        -- key already registered
        return false
    end

    mtable[key] = data
    updateMediaList(mediatype, key)
    self.callbacks:Fire("LibSharedMedia_Registered", mediatype, key)
    return true
end

function lib:Fetch(mediatype, key, noDefault)
    local mtt = mediaTable[mediatype]
    local overridekey = overrideMedia[mediatype]
    local result = mtt and ((overridekey and mtt[overridekey] or mtt[key]) or (not noDefault and defaultMedia[mediatype] and mtt[defaultMedia[mediatype]])) or nil
    return result ~= "" and result or nil
end

function lib:IsValid(mediatype, key)
    return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false
end

function lib:HashTable(mediatype)
    return mediaTable[mediatype]
end

function lib:List(mediatype)
    if not mediaTable[mediatype] then
        return nil
    end
    if not mediaList[mediatype] then
        buildMediaList(mediatype)
    end
    return mediaList[mediatype]
end

function lib:GetGlobal(mediatype)
    return overrideMedia[mediatype]
end

function lib:SetGlobal(mediatype, key)
    if not mediaTable[mediatype] then
        return false
    end
    overrideMedia[mediatype] = (key and mediaTable[mediatype][key]) and key or nil
    self.callbacks:Fire("LibSharedMedia_SetGlobal", mediatype, overrideMedia[mediatype])
    return true
end

function lib:GetDefault(mediatype)
    return defaultMedia[mediatype]
end

function lib:SetDefault(mediatype, key)
    if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then
        defaultMedia[mediatype] = key
        return true
    else
        return false
    end
end
