-- Generic useful variables
local VULPERA_playerUID = UnitGUID("player")
local VULPERA_settingsVersion = 1
local VULPERA_className, VULPERA_classFilename, VULPERA_classId = UnitClass("player")
local VULPERA_raceName = UnitRace("player")
local VULPERA_gender = UnitSex("player")
local VULPERA_path = "sounds_en\\"

-- Check that the user is actually playing a Vulpera.
if VULPERA_raceName ~= "Vulpera" then
	print("|cffffcc00VE: |rSeems like you are not a Vulpera right now! Vulpera Experience is disabled for this Character. :C")
    return nil
end

-- Selecting language Folder based on clients Language
if GetLocale() == "deDE" then
	VULPERA_path = "sounds_de\\"
end

------
-- Global settings variable
VULPERA_settings = {
    version = VULPERA_settingsVersion,
}

------
-- Default values for the settings
local VULPERA_settingsDefault = {
    initialized = true,
    version = VULPERA_settingsVersion,
     -- Global cooldown between each individual sound effect. (Can be overridden for Meta for instance)
    soundGlobalCooldown = 8, -- seconds
    -- Chance for each category of sound effect to trigger
    probabilityTable = {
        AFKEND = 1.0,
        AFKSTART = 1.0,
        AGGRO = 0.33,
        DEATH = 1.0,
        MOUNT = 0.33,
        REVIVE = 1.0,
        SELECT = 1.0,
        IDLE = 0.33,
    },
    -- Mapping of probabilities for each of the individual sounds
    -- If a sound is going to be played, then picked a weighted option from this table.
    -- The numbers in the table represents the number of sides on the "dice" that the option will be on.
    -- The total number of sides of the dice is the total of all rows in the table.
    -- So if all options are 1, then they are all equally likely. But if all are one, and one is two,
    -- then that one is doubly as likely as any other option. If an option is zero then it won't be rolled.
    soundProbabilityTable = {
        AFKEND = {
            ["afkend\\timetogo.ogg"] = 2,
            ["afkend\\camp.ogg"] = 1,
            ["afkend\\camp_alt.ogg"] = 1,
        },
        AFKSTART = {
            ["afkstart\\yawning.ogg"] = 1,
            ["afkstart\\goodplace.ogg"] = 3,
            ["afkstart\\stayawhile.ogg"] = 2,
            ["afkstart\\andlisten.ogg"] = 2,
        },
        AGGRO = {
            ["aggroed\\bequick.ogg"] = 2,
            ["aggroed\\theloot.ogg"] = 2,
            ["aggroed\\danger.ogg"] = 4,
            ["aggroed\\creepy.ogg"] = 1,
        },
        DEATH = {
            ["death\\dyingnoises.ogg"] = 3,
            ["death\\failed.ogg"] = 2,
            ["death\\terminapaw.ogg"] = 1,
        },
        MOUNT = {
            ["mount\\dollyanddot.ogg"] = 1,
            ["mount\\travel.ogg"] = 2,
            ["mount\\wagon.ogg"] = 2,
            ["mount\\find.ogg"] = 2,
        },
        REVIVE = {
            ["revived\\back.ogg"] = 1,
            ["revived\\coat.ogg"] = 1,
            ["revived\\preserved.ogg"] = 1,
            ["revived\\takecare.ogg"] = 1,
            ["revived\\toldyouso.ogg"] = 1,
        },
        SELECT = {
            ["select\\dundie.ogg"] = 2,
            ["select\\pointing.ogg"] = 1,
            ["select\\remember.ogg"] = 2,
        },
        IDLE = {
            ["idle\\1.ogg"] = 1,
            ["idle\\2.ogg"] = 1,
			["afkstart\\yawning.ogg"] = 1,
        },
    }
}

-- Variables for tracking the current player state
local VULPERA_playerIsLogin = nil
local VULPERA_playerInCombat = nil
local VULPERA_IdleCheck = C_Timer.NewTicker(25, function() VULPERA_onIdle() end)
local VULPERA_playerWasAFK = UnitIsAFK("player")
local VULPERA_playerWasMounted = IsMounted()
local VULPERA_currentSoundHandle = nil
local VULPERA_lastSoundTimestamp = nil
local VULPERA_lastFilenamePlayed = nil

function VULPERA_onIdle()
    local VULPERA_speed = GetUnitSpeed("player")
	local VULPERA_isFlying = IsFlying()
	local VULPERA_isDead = UnitIsDeadOrGhost("player")
	if VULPERA_speed == 0 and not VULPERA_isFlying and not VULPERA_playerInCombat and not VULPERA_isDead then
	    -- print("You idle!")
	    VULPERA_handleSoundEvent("IDLE", true)
	end
end

function VULPERA_isSoundCooldown()
    if not VULPERA_lastSoundTimestamp then
        return false
    end
    local currentTime = GetServerTime()
    
    return currentTime - VULPERA_lastSoundTimestamp < VULPERA_settings.soundGlobalCooldown
end

function VULPERA_handleSoundEvent(event, overrideCooldown)
    
    -- If we are on cooldown don't play anything.
    if VULPERA_isSoundCooldown() and not overrideCooldown then
        return nil
    end
    
    -- Stop the previous sound effect if its still playing. This is mostly useful for override sounds.
    if VULPERA_currentSoundHandle ~= nil then
        StopSound(VULPERA_currentSoundHandle, 0)
    end

    -- Roll to see if we want to play a sound
    local playSoundRoll = fastrandom()
    if playSoundRoll >= VULPERA_settings.probabilityTable[event] then
        return nil
    end

    -- We rolled to play the sound effect, now use the particular event's probability table to determine which one to play
    local probabilities = VULPERA_settings.soundProbabilityTable[event]

    -- Remove the previously played sound effect from the table so we don't roll it twice in a row.
    local originalProbability = nil
    if VULPERA_lastFilenamePlayed ~= nil then
        for filename, soundProbability in pairs(probabilities) do
            if filename == VULPERA_lastFilenamePlayed then
                originalProbability = soundProbability
                probabilities[filename] = 0
                break
            end
        end
    end

    -- First calculate the total number of sides of the dice to use
    local totalSides = 0
    for unused, soundProbability in pairs(probabilities) do
        totalSides = totalSides + soundProbability
    end

    -- if all sound options are disabled, then just return
    if totalSides == 0 then
        return nil
    end

    -- then determine which side of the die it landed on and play the sound
    local roll = fastrandom(1, totalSides)
    local cumulativeRange = 1
    local willPlaySound = false
    for filename, soundProbability in pairs(probabilities) do
        if (cumulativeRange <= roll) and (roll < cumulativeRange + soundProbability) then
            
            -- Restore the original probability to the previous sound played
            if originalProbability ~= nil and originalProbability > 0 then
                for f, s in pairs(probabilities) do
                    if f == VULPERA_lastFilenamePlayed then
                        probabilities[f] = originalProbability
                        break
                    end
                end
            end
            willPlaySound, VULPERA_currentSoundHandle = PlaySoundFile("Interface\\AddOns\\VulperaExperience\\" .. VULPERA_path .. filename, "Dialog")
            VULPERA_lastFilenamePlayed = filename
            VULPERA_lastSoundTimestamp = GetServerTime()
            break
        end
        cumulativeRange = cumulativeRange + soundProbability
    end
end

local VulperaExperience, VULPERA_events = CreateFrame("Frame"), {};

function VULPERA_events:PLAYER_REGEN_DISABLED(...)
    VULPERA_playerInCombat = true
    VULPERA_handleSoundEvent("AGGRO")
end

function VULPERA_events:PLAYER_REGEN_ENABLED(...)
    VULPERA_playerInCombat = false
end

function VULPERA_events:PLAYER_DEAD(...)
    VULPERA_handleSoundEvent("DEATH")
end

function VULPERA_events:PLAYER_UNGHOST(...)
    VULPERA_handleSoundEvent("REVIVE")
end

function VULPERA_events:PLAYER_ENTERING_WORLD(...)
    if not VULPERA_playerIsLogin then
	    -- This should only fire once per login and/or the first zone change then not again
	    -- except the player relogs e.g. back to character select and login's again!
		print("|cffffcc00VE: |cff00ccffThanks for using Vulpera Experience!|r")
		print("|cffffcc00VE: |cff00ccffI do hope you'll enjoy your journey with a little more immersion now. :3|r")
		print("|cffffcc00VE: |cff00ccffQuestions? Problems? Hit me up on Battle.net -> |cffffffffWoweePaw#2553|r")
		print("|cffffcc00VE: |cff00ccffFeel free to follow on |cffffffffTwitch.tv/WoweePaw ;3|r")
	    VULPERA_playerIsLogin = true
		VULPERA_handleSoundEvent("AFKEND")
	end
end

function VULPERA_events:UNIT_AURA(...)
    local target = ...
    if not VULPERA_playerWasMounted and IsMounted() then
        VULPERA_playerWasMounted = true
        VULPERA_handleSoundEvent("MOUNT")
    elseif VULPERA_playerWasMounted and not IsMounted() then
        VULPERA_playerWasMounted = false
    end
end

function VULPERA_events:PLAYER_TARGET_CHANGED(...)
    if UnitGUID("target") == VULPERA_playerUID then
        VULPERA_handleSoundEvent("SELECT")
    end
end

function VULPERA_events:PLAYER_FLAGS_CHANGED(...)
    if not VULPERA_playerWasAFK and UnitIsAFK("player") then
        VULPERA_playerWasAFK = true
        VULPERA_handleSoundEvent("AFKSTART")
    elseif VULPERA_playerWasAFK and not UnitIsAFK("player") then
        VULPERA_playerWasAFK = false
        VULPERA_handleSoundEvent("AFKEND")
    end
end

function VULPERA_events:ADDON_LOADED(...)
    local addonName = ...
    if addonName == "VulperaExperience" then
        -- Update the settings with the new version. TODO don't override changed settings.
        if not VULPERA_settings.initialized then
            VULPERA_settings = VULPERA_settingsDefault
        end
        if VULPERA_settings.version ~= VULPERA_settingsVersion then
            VULPERA_settings = VULPERA_settingsDefault
        end
    end
end

-- Generic event handler that will call our internal event handlers. Each interal event handler will then filter for the
-- individual events that we want (like attack or meta for instance) and call the handleSoundEvent function
VulperaExperience:SetScript("OnEvent", function(self, event, ...)
    VULPERA_events[event](self, ...);
end);

-- Register all events for which handlers have been defined
for k, v in pairs(VULPERA_events) do
    VulperaExperience:RegisterEvent(k);
end
