-- Yoco's "QuestSounds" addon
--  Provides an audio cue on the following events:
--  - completing a quest
--  - completing one of the objectives of a quest (without fully completing that quest)
--  - gaining progress for one of the objectives of a quest (without completing that objective)
-- $Date: 2012/09/12 18:09:57 $ $Revision: 1.8 $

-- Sound set definitions
local soundSets = {
  gong = {
    questComplete = "Sound/Doodad/G_GongTroll01.ogg",
    objectiveComplete = "Sound/Doodad/G_BearTrapReverse_Close01.ogg",
    objectiveProgress = "Sound/Spells/Bonk1.ogg",
  },
  wacky = { -- this set acts as default for new installations
    questComplete = "Sound/Doodad/Goblin_Lottery_Open02.ogg",
    objectiveComplete = "Sound/Events/UD_DiscoBallSpawn.ogg",
    objectiveProgress = "Sound/Spells/Bonk1.ogg",
  },
  creatures = {
    questComplete = "Sound/Creature/Chicken/ChickenDeathA.ogg",
    objectiveComplete = "Sound/Creature/Frog/FrogFootstep2.ogg",
    objectiveProgress = "Sound/Creature/Crab/CrabWoundC.ogg",
  }
}

-- The saved variables
QSndVars = {} -- will be initialized in PLAYER_LOGIN

-- The sounds to play
local sounds = nil -- will be initialized in PLAYER_LOGIN

-- The message level codes
local messageLevels = {
  -- increment in multiples of 10 so we can add leels later without breaking stuff
  none = 0,
  completion = 10,
  objectives = 20,
  all = 30
}

local me = CreateFrame("Frame")
local events = {}

me.questIndex = 0
me.questId = 0
me.completeCount = 0
me.msgLvl = messageLevels.all

local function countCompleteObjectives(index)
  local n = 0
  for i = 1, GetNumQuestLeaderBoards(index) do
    local _, _, finished = GetQuestLogLeaderBoard(i, index)
    if finished then
      n = n + 1
    end
  end
  return n
end

function me:setQuest(index)
  self.questIndex = index
  if index>0 then
    local _, _, _, _, _, _, _, _, id = GetQuestLogTitle(index)
    self.questId = id
    if id and id>0 then
      self.completeCount = countCompleteObjectives(index)
    end
  end
end

function me:checkQuest()
  if self.questIndex>0 then
    local index = self.questIndex
    self.questIndex = 0
    local title, level, _, _, _, _, complete, daily, id = GetQuestLogTitle(index)
    local link = GetQuestLink(index)
    if id==self.questId then
      if id and id>0 then
        local objectivesComplete = countCompleteObjectives(index)
        if complete then
          if me.msgLvl >= messageLevels.completion then
            print("["..level.."] '"..link.."': complete");
          end
          self:Play(sounds and sounds.questComplete)
        elseif objectivesComplete>self.completeCount then
          if me.msgLvl >= messageLevels.objectives then
            print("["..level.."] '"..link.."': objective complete ("..objectivesComplete.."/"..GetNumQuestLeaderBoards(index)..")");
          end
          self:Play(sounds and sounds.objectiveComplete)
        else
          if me.msgLvl >= messageLevels.all then
            print("["..level.."] '"..link.."': updated");
          end
          self:Play(sounds and sounds.objectiveProgress)
        end
      end
    end
  end
end

function me:init()
  self:SetScript("OnEvent", function(frame, event, ...)
    local handler = events[event]
    if handler then
      -- dispatch events that were auto-registered by naming convention
      handler(frame, ...)
    end
  end)
  for k,v in pairs(events) do
    self:RegisterEvent(k)
  end
end

function me:Play(sound)
  -- print("Playing:", sound)
  if sound and sound~="" then
    PlaySoundFile(sound);
  end
end

function me:SetSoundSet(setName, silent)
  if setName and soundSets[setName] then
    QSndVars.ActiveSet = setName
    sounds = soundSets[setName]
    if not(silent) then
      print("Switching to sound set |cFF44FF00"..setName.."|r")
      me:Play(sounds.questComplete)
    end
  else
    print("Recognized sound set names are:")
    for k,v in pairs(soundSets) do
      if k==QSndVars.ActiveSet then
        print("  "..k.."      (*)")
      else
        print("  "..k)
      end
    end
  end
end

function me:SetMessageLevel(messageLevel)
  local levelCode = messageLevels[messageLevel]
  if levelCode==nil then
    -- ignore silently now
  else
    QSndVars.Messages = messageLevel
    me.msgLvl = levelCode
  end
end


function me:SetMessageLevelCmd(messageLevel)
  if messageLevel==nil or messageLevel=="" then
    print("The current message verbosity level is '"..QSndVars.Messages.."'")
  else
    local levelCode = messageLevels[messageLevel]
    if levelCode==nil then
      print("Unrecognized message verbosity level '"..messageLevel.."'. Expecting one of :")
      for k,v in pairs(messageLevels) do
        print("  "..k);
      end
    else
      me:SetMessageLevel(messageLevel)
      print("Setting message verbosity to "..QSndVars.Messages)
    end
  end
end


-- ................................................................
-- event handlers

function events:UNIT_QUEST_LOG_CHANGED(unit)
  if unit=="player" then
    me:checkQuest()
  end
end

function events:QUEST_WATCH_UPDATE(index)
  -- This event triggers just *before* the changes are registered
  -- in the quest log, giving a great opportunity to quantify changes
  -- in the subsequent UNIT_QUEST_LOG_CHANGED
  me:setQuest(index)
end

function events:PLAYER_LOGIN()
  -- Saved variables are stable now
  if not(QSndVars.ActiveSet) then
    QSndVars.ActiveSet = "wacky"
  end
  if not(QSndVars.Messages) then
    QSndVars.Messages = "all"
    -- one of "all", "objectives", "completion", "none"
  end
  me:SetMessageLevel(QSndVars.Messages)
  me:SetSoundSet(QSndVars.ActiveSet, true)
end

-- ................................................................
-- command interface

SLASH_QUESTSOUNDS1 = "/qsnd"
SLASH_QUESTSOUNDS2 = "/questsounds"

function SlashCmdList.QUESTSOUNDS(msg, editbox)
  local cmd, rest = msg:match("^(%S*)%s*(.-)$")
  me:Command(cmd, rest)
end

function me:Command(command, arg)
  local handler = self["CMD"..command]
  if handler then
    handler(self, arg)
  else
    if command~="" then
      print("Unknown questsounds command '"..command.."'");
    end
    print("  /qsnd set <soundsetname>")
    print("     Sets the sound set and plays its quest completion sound")
    print("  /qsnd set")
    print("     Lists the known sound set names")
    print("  /qsnd test <quest|objective|part>")
    print("     Plays the current 'quest', 'objective' or 'part' completion sound")
    print("  /qsnd message <all|objectives|completion|none>")
    print("     Sets verbosity of messages in the chat log to 'all', 'objectives',")
    print("     'completion' or 'none'")
    print("  The current sound set is |cFF44FF00"..QSndVars.ActiveSet.."|r");
    print("  The current message verbosity is |cFF44FF00"..QSndVars.Messages.."|r");
  end
end

function me:CMDtest(arg)
  if arg=="1" or arg=="quest" then
    print("Playing |cFFCCCC44quest complete|r sound of sound set |cFF44FF00"..QSndVars.ActiveSet.."|r")
    self:Play(sounds and sounds.questComplete)
  elseif arg=="2" or arg=="objective" then
    print("Playing |cFFCCCC44objective complete|r sound of sound set |cFF44FF00"..QSndVars.ActiveSet.."|r")
    self:Play(sounds and sounds.objectiveComplete)
  elseif arg=="3" or arg=="part" then
    print("Playing |cFFCCCC44objective progress|r sound of sound set |cFF44FF00"..QSndVars.ActiveSet.."|r")
    self:Play(sounds and sounds.objectiveProgress)
  else
    print("  Usage: /qsnd test <sound id>")
    print("    Recognized values for <sound id> are:")
    print("      '1' or 'quest' for the quest complete sound")
    print("      '2' or 'objective' for the objective complete sound")
    print("      '3' or 'part' for the objective part update sound")
  end
end

function me:CMDset(arg)
  self:SetSoundSet(arg)
end

function me:CMDmessage(arg)
  self:SetMessageLevelCmd(arg)
end

function me:CMDmsg(arg)
  self:SetMessageLevelCmd(arg)
end

-- ................................................................
-- must be last line:
me:init()
