if SERVER then AddCSLuaFile() end
local Taunts = {}
local Categories = {}
local function InsertCategory(category)
    local id = #Categories + 1
    Categories[id] = {
        Id = id,
        Name = category
    }
    return id
end

local function GetCategoryIndex(category)
    for k, v in pairs(Categories) do
        if v.Name == category then return v.Id end
    end
    return nil
end

local function InsertTaunt(categoryId, mdlCategory, name, files)
    if Categories[categoryId] == nil then
        error("Invalid category id.")
        return
    end

    Taunts[categoryId] = Taunts[categoryId] or {}
    Taunts[categoryId][mdlCategory] = Taunts[categoryId][mdlCategory] or {}
    local data = {
        Name = name,
        Sounds = files
    }

    table.insert(Taunts[categoryId][mdlCategory], data)
end

local CAT_TAUNTS = InsertCategory("Taunts")
local function InsertMultipleTaunts(categoryId, mdlCategory, tauntsTable)
    for _, data in pairs(tauntsTable) do
        local name = data[1]
        local sounds = data[2]
        InsertTaunt(categoryId, mdlCategory, name, type(sounds) == "table" and sounds or {sounds})
    end
end

local ZOMBIE_MOANS = {
    "npc/zombie/zombie_voice_idle1.wav",
    "npc/zombie/zombie_voice_idle2.wav",
    "npc/zombie/zombie_voice_idle3.wav",
    "npc/zombie/zombie_voice_idle4.wav",
    "npc/zombie/zombie_voice_idle5.wav",
    "npc/zombie/zombie_voice_idle6.wav",
    "npc/zombie/zombie_voice_idle7.wav",
    "npc/zombie/zombie_voice_idle8.wav",
    "npc/zombie/zombie_voice_idle9.wav",
    "npc/zombie/zombie_voice_idle10.wav",
    "npc/zombie/zombie_voice_idle11.wav",
    "npc/zombie/zombie_voice_idle12.wav",
    "npc/zombie/zombie_voice_idle13.wav",
    "npc/zombie/zombie_voice_idle14.wav",
}
InsertMultipleTaunts(CAT_TAUNTS, "zombie", {
    { "Over there", "npc/zombie/zombie_alert1.wav" },
    { "Over here", "npc/zombie/zombie_alert2.wav" },
    { "Take cover", ZOMBIE_MOANS },
    { "I'm Ready", ZOMBIE_MOANS },
    { "Hi", ZOMBIE_MOANS },
    { "Yeah", ZOMBIE_MOANS },
    { "Okay", ZOMBIE_MOANS },
    { "No", ZOMBIE_MOANS },
    { "Nice", ZOMBIE_MOANS },
    { "Help", ZOMBIE_MOANS },
    { "Sorry", ZOMBIE_MOANS },
    { "Leave it alone", ZOMBIE_MOANS },
    { "I'm with you", ZOMBIE_MOANS },
    { "Ready when you are", ZOMBIE_MOANS },
    { "Whatever you say", ZOMBIE_MOANS },
    { "Let's go", ZOMBIE_MOANS },
    { "Excuse me", ZOMBIE_MOANS },
    { "Get down", ZOMBIE_MOANS },
    { "Heads up", ZOMBIE_MOANS },
    { "Fantastic", ZOMBIE_MOANS },
    { "Finally", ZOMBIE_MOANS },
    { "Good God", ZOMBIE_MOANS },
    { "Run for your life", ZOMBIE_MOANS },
    { "Run", ZOMBIE_MOANS },
    { "Behind you", ZOMBIE_MOANS },
    { "You got it", ZOMBIE_MOANS },
    { "Whoops", ZOMBIE_MOANS },
    { "Watch out", ZOMBIE_MOANS },
    { "Waiting for somebody?", ZOMBIE_MOANS },
    { "Like that", ZOMBIE_MOANS },
    { "We trusted you", ZOMBIE_MOANS },
    { "How about that", ZOMBIE_MOANS },
    { "Wanna bet", ZOMBIE_MOANS },
})

InsertMultipleTaunts(CAT_TAUNTS, "combine", {
    { "Over there", { "npc/metropolice/vo/move.wav", "npc/metropolice/vo/moveit.wav", "npc/metropolice/vo/moveit2.wav", "npc/metropolice/vo/examine.wav"} },
    { "Over here", { "npc/metropolice/vo/holdthisposition.wav", "npc/metropolice/vo/holditrightthere.wav"} },
    { "Take cover", "npc/metropolice/vo/takecover.wav" },
    { "I'm Ready", { "npc/metropolice/vo/readytojudge.wav", "npc/metropolice/vo/readytoprosecute.wav", "npc/metropolice/vo/readytoamputate.wav"} },
    { "Affirmative", { "npc/combine_soldier/vo/affirmative.wav", "npc/combine_soldier/vo/affirmative2.wav"} },
    { "Nice", "npc/metropolice/vo/chuckle.wav" },
    { "Help", "npc/metropolice/vo/help.wav" },
})

InsertMultipleTaunts(CAT_TAUNTS, "alyx", {
    { "Over there", {"vo/k_lab/al_there.wav", "vo/novaprospekt/al_there.wav"} },
    { "Over here", "vo/trainyard/al_overhere.wav" },
    { "Take cover", "vo/npc/female01/takecover02.wav" },
    { "Follow me", "vo/novaprospekt/al_followme01.wav" },
    { "Let's get going", "vo/novaprospekt/al_letsgetgoing.wav" },
    { "Cover me", {"vo/npc/alyx/coverme01.wav", "vo/npc/alyx/coverme02.wav", "vo/npc/alyx/coverme03.wav"} },
    { "Let's go", {"vo/streetwar/alyx_gate/al_letsgo.wav", "vo/streetwar/alyx_gate/al_letsgo01.wav"} },
    { "Excuse me", "vo/npc/alyx/al_excuse03.wav" },
    { "Watch out", {"vo/npc/alyx/watchout01.wav", "vo/npc/alyx/watchout02.wav"} },
})
InsertMultipleTaunts(CAT_TAUNTS, "monk", {
    { "Over there", "vo/ravenholm/exit_goquickly.wav" },
    { "Over here", {"vo/ravenholm/shotgun_overhere.wav", "vo/ravenholm/monk_overhere.wav"} },
    { "Take cover", "vo/ravenholm/bucket_guardwell.wav" },
    { "Follow me", {"vo/ravenholm/monk_followme.wav", "vo/ravenholm/grave_stayclose.wav"} },
    { "Cover me", {"vo/ravenholm/monk_coverme01.wav", "vo/ravenholm/monk_coverme02.wav", "vo/ravenholm/monk_coverme03.wav", "vo/ravenholm/monk_coverme04.wav", "vo/ravenholm/monk_coverme05.wav", "vo/ravenholm/monk_coverme07.wav"} },
    { "Let's go", "vo/ravenholm/exit_goquickly.wav" },
    { "Watch out", {"vo/ravenholm/monk_danger03.wav", "vo/ravenholm/monk_danger02.wav", "vo/ravenholm/monk_danger01.wav"} },
    { "Behind you", "vo/ravenholm/firetrap_lookout.wav" },
    { "Help", {"vo/ravenholm/monk_helpme01.wav", "vo/ravenholm/monk_helpme02.wav", "vo/ravenholm/monk_helpme04.wav", "vo/ravenholm/monk_helpme05.wav"} }
})

InsertMultipleTaunts(CAT_TAUNTS, "barney", {
    { "Over there", "vo/streetwar/sniper/ba_letsgetgoing.wav" },
    { "Over here", "vo/streetwar/sniper/ba_overhere.wav" },
    { "Follow me", {"vo/npc/barney/ba_followme02.wav", "vo/npc/barney/ba_followme05.wav"} },
    { "Let's go", "vo/npc/barney/ba_letsgo.wav" },
    { "Watch out", "vo/npc/barney/ba_lookout.wav" },
    { "Help", "vo/streetwar/rubble/ba_helpmeout.wav" },
    { "Done", "vo/streetwar/nexus/ba_done.wav" }
})

InsertMultipleTaunts(CAT_TAUNTS, "male", {
    { "Over there", {"vo/npc/male01/overthere01.wav", "vo/npc/male01/overthere02.wav"} },
    { "Over here", "vo/npc/male01/overhere01.wav" },
    { "Take cover", "vo/npc/male01/takecover02.wav" },
    { "I'm Ready", {"vo/npc/male01/okimready01.wav", "vo/npc/male01/okimready02.wav", "vo/npc/male01/okimready03.wav"} },
    { "Hi", {"vo/npc/male01/hi01.wav", "vo/npc/male01/hi02.wav"} },
    { "Yeah", "vo/npc/male01/yeah02.wav" },
    { "Okay", {"vo/npc/male01/ok01.wav", "vo/npc/male01/ok02.wav"} },
    { "No", "vo/npc/male01/no01.wav" },
    { "Nice", "vo/npc/male01/nice.wav" },
    { "Help", "vo/npc/male01/help01.wav" },
    { "Sorry", {"vo/npc/male01/sorry01.wav", "vo/npc/male01/sorry02.wav", "vo/npc/male01/sorry03.wav"} },
    { "Leave it alone", "vo/npc/male01/answer38.wav" },
    { "I'm with you", "vo/npc/male01/answer13.wav" },
    { "Ready when you are", {"vo/npc/male01/readywhenyouare01.wav", "vo/npc/male01/readywhenyouare02.wav"} },
    { "Whatever you say", "vo/npc/male01/squad_affirm03.wav" },
    { "Let's go", {"vo/npc/male01/letsgo01.wav", "vo/npc/male01/letsgo02.wav"} },
    { "Excuse me", {"vo/npc/male01/excuseme01.wav", "vo/npc/male01/excuseme02.wav"} },
    { "Get down", "vo/npc/male01/getdown02.wav" },
    { "Heads up", {"vo/npc/male01/headsup01.wav", "vo/npc/male01/headsup02.wav"} },
    { "Fantastic", {"vo/npc/male01/fantastic01.wav", "vo/npc/male01/fantastic02.wav"} },
    { "Finally", "vo/npc/male01/finally.wav" },
    { "Good God", "vo/npc/male01/goodgod.wav" },
    { "Run for your life", {"vo/npc/male01/runforyourlife01.wav", "vo/npc/male01/runforyourlife02.wav", "vo/npc/male01/runforyourlife03.wav"} },
    { "Run", "vo/npc/male01/strider_run.wav" },
    { "Behind you", {"vo/npc/male01/behindyou01.wav", "vo/npc/male01/behindyou02.wav"} },
    { "You got it", "vo/npc/male01/yougotit02.wav" },
    { "Whoops", "vo/npc/male01/whoops01.wav" },
    { "Watch out", "vo/npc/male01/watchout.wav" },
    { "Waiting for somebody?", "vo/npc/male01/waitingsomebody.wav" },
    { "Like that", "vo/npc/male01/likethat.wav" },
    { "We trusted you", {"vo/npc/male01/wetrustedyou01.wav", "vo/npc/male01/wetrustedyou02.wav"} },
    { "How about that", "vo/npc/male01/answer25.wav" },
    { "Wanna bet", "vo/npc/male01/answer27.wav" }
})

InsertMultipleTaunts(CAT_TAUNTS, "female", {
    { "Over there", {"vo/npc/female01/overthere01.wav", "vo/npc/female01/overthere02.wav"} },
    { "Over here", "vo/npc/female01/overhere01.wav" },
    { "Take cover", "vo/npc/female01/takecover02.wav" },
    { "I'm Ready", {"vo/npc/female01/okimready01.wav", "vo/npc/female01/okimready02.wav", "vo/npc/female01/okimready03.wav"} },
    { "Hi", {"vo/npc/female01/hi01.wav", "vo/npc/female01/hi02.wav"} },
    { "Yeah", "vo/npc/female01/yeah02.wav" },
    { "Okay", {"vo/npc/female01/ok01.wav", "vo/npc/female01/ok02.wav"} },
    { "No", "vo/npc/female01/no01.wav" },
    { "Nice", {"vo/npc/female01/nice01.wav", "vo/npc/female01/nice02.wav"} },
    { "Help", "vo/npc/female01/help01.wav" },
    { "Sorry", {"vo/npc/female01/sorry01.wav", "vo/npc/female01/sorry02.wav", "vo/npc/female01/sorry03.wav"} },
    { "Leave it alone", "vo/npc/female01/answer38.wav" },
    { "I'm with you", "vo/npc/female01/answer13.wav" },
    { "Ready when you are", {"vo/npc/female01/readywhenyouare01.wav", "vo/npc/female01/readywhenyouare02.wav"} },
    { "Whatever you say", "vo/npc/female01/squad_affirm03.wav" },
    { "Let's go", {"vo/npc/female01/letsgo01.wav", "vo/npc/female01/letsgo02.wav"} },
    { "Excuse me", {"vo/npc/female01/excuseme01.wav", "vo/npc/female01/excuseme02.wav"} },
    { "Get down", "vo/npc/female01/getdown02.wav" },
    { "Heads up", {"vo/npc/female01/headsup01.wav", "vo/npc/female01/headsup02.wav"} },
    { "Fantastic", {"vo/npc/female01/fantastic01.wav", "vo/npc/female01/fantastic02.wav"} },
    { "Finally", "vo/npc/female01/finally.wav" },
    { "Good God", "vo/npc/female01/goodgod.wav" },
    { "Run for your life", {"vo/npc/female01/runforyourlife01.wav", "vo/npc/female01/runforyourlife02.wav", "vo/npc/female01/runforyourlife03.wav"} },
    { "Run", "vo/npc/female01/strider_run.wav" },
    { "Behind you", {"vo/npc/female01/behindyou01.wav", "vo/npc/female01/behindyou02.wav"} },
    { "You got it", "vo/npc/female01/yougotit02.wav" },
    { "Whoops", "vo/npc/female01/whoops01.wav" },
    { "Watch out", "vo/npc/female01/watchout.wav" },
    { "Waiting for somebody?", "vo/npc/female01/waitingsomebody.wav" },
    { "Like that", "vo/npc/female01/likethat.wav" },
    { "We trusted you", {"vo/npc/female01/wetrustedyou01.wav", "vo/npc/female01/wetrustedyou02.wav"} },
    { "How about that", "vo/npc/female01/answer25.wav" },
    { "Wanna bet", "vo/npc/female01/answer27.wav" }
})

InsertMultipleTaunts(CAT_TAUNTS, "gman", {
    { "Over there", "vo/outland_02/junction/gman_mono30.wav" },
    { "Over here", { "vo/outland_02/junction/gman_mono04.wav", "vo/outland_02/junction/gman_mono28.wav"} },
    { "Well", "vo/outland_02/junction/gman_mono29.wav" },
})

InsertMultipleTaunts(CAT_TAUNTS, "breen", {
    { "Over there", "vo/breencast/br_tofreeman12.wav" },
    { "Over here", "vo/breencast/br_welcome07.wav" },
    { "No", {"vo/citadel/br_no.wav", "vo/citadel/br_failing11.wav"} },
    { "Eli", "vo/citadel/br_playgame_a.wav" },
    { "Shit", "vo/citadel/br_ohshit.wav" },
    { "You fool", "vo/citadel/br_youfool.wav" },
    { "You need me", "vo/citadel/br_youneedme.wav" },
    { "Laugh", "vo/citadel/br_laugh01.wav" },
})

InsertMultipleTaunts(CAT_TAUNTS, "vortigaunt", {
    { "Over there", "vo/npc/vortigaunt/forward.wav" },
    { "Over here", "vo/npc/vortigaunt/here.wav" },
    { "Yes", "vo/npc/vortigaunt/yes.wav" },
    { "The Freeman", "vo/npc/vortigaunt/freeman.wav" },
    { "All For Freedom", "vo/npc/vortigaunt/forfreedom.wav" },
    { "We Serve The Same Mystery", "vo/npc/vortigaunt/mystery.wav" },
    { "Our Finest Poet", "vo/npc/vortigaunt/poet.wav" },
    { "We Are Yours", "vo/npc/vortigaunt/weareyours.wav" },
    { "The Eli Vance", "vo/eli_lab/vort_elab_use01.wav" },
    { "Vortigese", {"vo/npc/vortigaunt/vortigese02.wav"} },
    { "Hold Still", "vo/npc/vortigaunt/holdstill.wav" },
    { "Caution", "vo/npc/vortigaunt/caution.wav" },
    { "To The Void", "vo/npc/vortigaunt/tothevoid.wav" },
    { "Stand Clear", "vo/npc/vortigaunt/standclear.wav" },
    { "Accompany", "vo/npc/vortigaunt/accompany.wav" },
    { "Greetings", "vo/npc/vortigaunt/greetingsfm.wav" },
    { "Satisfaction", "vo/npc/vortigaunt/satisfaction.wav" },
    { "As You Wish", "vo/npc/vortigaunt/asyouwish.wav" },
    { "Certainly", "vo/npc/vortigaunt/certainly.wav" },
    { "Gladly", "vo/npc/vortigaunt/gladly.wav" },
    { "With Pleasure", "vo/npc/vortigaunt/pleasure.wav" },
    { "We Remember The Freeman", "vo/npc/vortigaunt/vmono_03.wav" },
    { "No Distance Between Us", "vo/npc/vortigaunt/vmono_04.wav" },
    { "Unity of Purpose", "vo/npc/vortigaunt/vmono_10.wav" },
    { "Your Song We Sing", "vo/npc/vortigaunt/vmono_11.wav" },
    { "Grief and Jubilation", "vo/npc/vortigaunt/vmono_12.wav" },
    { "Talisman of Victory", "vo/npc/vortigaunt/vmono_16.wav" },
    { "We Take Our Stand", "vo/npc/vortigaunt/vmono_24.wav" },
    { "We Are You, Freeman", "vo/npc/vortigaunt/vmono_30.wav" }
})

InsertMultipleTaunts(CAT_TAUNTS, "eli", {
    { "Over there", { "vo/outland_11a/silo/eli_silo_hurry.wav", "vo/outland_11a/silo/eli_silo_rightaway.wav", "vo/outland_11a/silo/eli_silo_thiswayg01.wav"} },
    { "Over here", "vo/outland_11a/silo/eli_silo_heardclose01.wav" },
    { "I know", "vo/outland_11a/silo/eli_silo_iknowiknow.wav" },
    { "Okay", "vo/outland_11a/silo/eli_silo_catchingup01.wav" },
    { "Good", "vo/outland_11a/silo/eli_silo_cuedup01.wav" },
    { "Laugh", {"vo/outland_11a/silo/eli_silo_chuckles02.wav", "vo/outland_11a/silo/eli_silo_chuckles04.wav"} }
})

InsertMultipleTaunts(CAT_TAUNTS, "kleiner", {
    { "Over there", { "vo/k_lab/kl_gordongo.wav", "vo/outland_11a/silo/kl_silo_somethingelse02.wav" } },
    { "Over here", {"vo/outland_11a/silo/kl_silo_somethingelse01.wav"} },
    { "Yes", {"vo/outland_12a/launch/kl_launch_awe05.wav", "vo/outland_11a/silo/kl_silo_yesyes.wav"} },
    { "Right", "vo/k_lab/kl_fewmoments01.wav" },
    { "Oh dear", "vo/k_lab/kl_ohdear.wav" },
    { "Incredible", "vo/outland_11a/silo/kl_silo_incredible.wav" },
    { "Fiddlesticks", "vo/k_lab/kl_fiddlesticks.wav" },
    { "Lamarr", "vo/k_lab/kl_hedyno01.wav" },
    { "Wish I Knew", "vo/k_lab/kl_wishiknew.wav" }
})

function GM:GetTauntCategories()
    return Categories
end

function GM:GetTauntCategoryId(category)
    return GetCategoryIndex(category)
end

function GM:GetTaunts(categoryId, mdlCategory)
    return Taunts[categoryId][mdlCategory]
end

function GM:GetPlayerTaunts(ply, categoryId)
    local mdlCategory = ply:GetModelCategory()
    return self:GetTaunts(categoryId, mdlCategory)
end

function GM:GetAvailableTauntCategories()
    local categories = {"auto"}
    for _, categoryTaunts in pairs(Taunts) do
        for mdlCategory, _ in pairs(categoryTaunts) do
            if not table.HasValue(categories, mdlCategory) then table.insert(categories, mdlCategory) end
        end
    end
    return categories
end