-- AutoQuestLearner Quest Module
-- Rebranded from pfQuest quest.lua
-- Handles quest log tracking, quest updates, and integration with map/tracker

AutoQuestLearner = AutoQuestLearner or CreateFrame("Frame")
AutoQuestLearner.icons = {}

AutoQuestLearner.dburl = "https://www.wowhead.com/classic/quest="

function AutoQuestLearner:Debug(msg)
  if not AutoQuestLearnerDB.config.debug then return end
  DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00AutoQuestLearner Debug:|r " .. (msg or ""))
end

function AutoQuestLearner:SortedPairs(t, index, reverse)
  local keys = {}
  for k, v in pairs(t) do
    if v then keys[table.getn(keys)+1] = k end
  end

  local order
  if reverse then
    order = function(t,a,b) return t[a][index] < t[b][index] end
  else
    order = function(t,a,b) return t[a][index] > t[b][index] end
  end
  table.sort(keys, function(a,b) return order(t, a, b) end)

  local i = 0
  return function()
    i = i + 1
    if keys[i] then
      return keys[i], t[keys[i]]
    end
  end
end

AutoQuestLearner.queue = {}
AutoQuestLearner.abandon = ""
AutoQuestLearner.questlog = {}
AutoQuestLearner.questlog_tmp = {}

local function tsize(tbl)
  if not tbl or not type(tbl) == "table" then return 0 end
  local c = 0
  for _ in pairs(tbl) do c = c + 1 end
  return c
end

AutoQuestLearner:RegisterEvent("QUEST_WATCH_UPDATE")
AutoQuestLearner:RegisterEvent("QUEST_LOG_UPDATE")
AutoQuestLearner:RegisterEvent("QUEST_FINISHED")
AutoQuestLearner:RegisterEvent("PLAYER_LEVEL_UP")
AutoQuestLearner:RegisterEvent("PLAYER_ENTERING_WORLD")
AutoQuestLearner:RegisterEvent("SKILL_LINES_CHANGED")
AutoQuestLearner:RegisterEvent("ADDON_LOADED")
AutoQuestLearner:SetScript("OnEvent", function()
  if event == "ADDON_LOADED" then
    if arg1 == "AutoQuestLearner" then
      AutoQuestLearner:AddQuestLogIntegration()
      AutoQuestLearner:AddWorldMapIntegration()
      this.lock = GetTime() + 10
    else
      return
    end
  elseif event == "SKILL_LINES_CHANGED" then
    local skills = ""
    for i=0, GetNumSkillLines() do
      skills = skills .. (GetSkillLineInfo(i) or "")
    end

    if skills ~= this.skillstate then
      AutoQuestLearner.updateQuestGivers = true
      this.skillstate = skills
    end
  elseif event == "PLAYER_LEVEL_UP" or event == "PLAYER_ENTERING_WORLD" then
    AutoQuestLearner.updateQuestGivers = true
  else
    AutoQuestLearner.updateQuestLog = true
  end
end)

AutoQuestLearner:SetScript("OnUpdate", function()
  if this.lock and this.lock > GetTime() then return end
  if not AutoQuestLearnerDB.localized then return end

  if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end

  if ( this.qlogtick or 1) < GetTime() then
    if AutoQuestLearner:UpdateQuestlog() then
      AutoQuestLearner:Debug("Update Quest Log")
      -- The tracker's ButtonAdd() only shows a quest once it exists in
      -- AutoQuestLearner.questlog (just rebuilt above). Previously
      -- nothing re-ran the tracker after this cache updated, so newly
      -- accepted quests could be silently skipped by a Reset() that had
      -- already run before the cache caught up, and nothing would ever
      -- retry it. Refresh here, right after the cache is confirmed current.
      if AutoQuestLearnerMap then
        AutoQuestLearnerMap:UpdateNodes()
      end
    end
    this.qlogtick = GetTime() + 3
  end

  if this.updateQuestLog == true and tsize(this.queue) == 0 then
    if (this.lastUpdateQuestLog or 0) < GetTime() then
      AutoQuestLearner:Debug("Update Quest Log")
      AutoQuestLearner:UpdateQuestlog()
      this.updateQuestLog = false
      -- BUGFIX: was 3 seconds -- meant up to a 3s delay after accepting
      -- a quest before this addon even noticed it exists, which is
      -- part of why the arrow kept pointing at the (now irrelevant)
      -- quest giver for a while after accepting instead of immediately
      -- switching. Still throttled (Blizzard fires QUEST_LOG_UPDATE
      -- repeatedly during a single accept/turn-in), just less
      -- sluggishly so.
      this.lastUpdateQuestLog = GetTime() + 1
    end
  end

  if this.updateQuestGivers == true then
    if (this.lastQuestGiverScan or 0) < GetTime() then
      AutoQuestLearner:Debug("Update Quest Givers")
      if AutoQuestLearnerDB.config.allquestgivers == "1" then
        local meta = { ["addon"] = "AUTOQUESTLEARNER" }
        if AutoQuestLearner.db then
          AutoQuestLearner.db:SearchQuests(meta)
        end
      end
      if AutoQuestLearner_RefreshAvailableGiverCache then
        AutoQuestLearner_RefreshAvailableGiverCache()
      end
      this.updateQuestGivers = false
      this.lastQuestGiverScan = GetTime() + 3
    end
  end

  if tsize(this.queue) == 0 then return end

  for id, entry in pairs(this.queue) do
    if entry[4] == "REMOVE" then
      AutoQuestLearner:Debug("Remove Quest: " .. entry[1] .. " (" .. entry[2] .. ")")

      if entry[1] == AutoQuestLearner.abandon then
        AutoQuestLearnerDB.history = AutoQuestLearnerDB.history or {}
        AutoQuestLearnerDB.history[entry[2]] = nil
        AutoQuestLearnerDB.history[entry[1]] = nil
      else
        AutoQuestLearnerDB.history = AutoQuestLearnerDB.history or {}
        -- Written under both the resolved quest key (entry[2], which
        -- can be numeric or a title string depending on how GetQuestIDs
        -- resolved it -- see AutoQuestLearner.lua's alias-lookup notes)
        -- AND the raw title (entry[1], always available) so a lookup
        -- by either form finds it.
        AutoQuestLearnerDB.history[entry[2]] = { time(), UnitLevel("player") }
        AutoQuestLearnerDB.history[entry[1]] = { time(), UnitLevel("player") }
      end

      if AutoQuestLearnerMap then
        AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", entry[1])
      end

      if AutoQuestLearnerData.quests and AutoQuestLearnerData.quests["loc"] and AutoQuestLearnerData.quests["loc"][entry[2]] then
        if AutoQuestLearnerMap then
          AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", AutoQuestLearnerData.quests["loc"][entry[2]].T)
        end
      end

      -- Belt-and-suspenders: also remove by questID directly, in case
      -- this quest's nodes were drawn under yet another title spelling
      -- (e.g. a gossip/quest-dialog title that doesn't exactly match
      -- either the quest-log title or the bundled database's title) --
      -- otherwise those nodes never match either DeleteNode() call
      -- above and are left behind on the map permanently.
      if AutoQuestLearnerMap and AutoQuestLearnerMap.DeleteNodeByQuestID and entry[2] then
        AutoQuestLearnerMap:DeleteNodeByQuestID("AUTOQUESTLEARNER", entry[2], entry[1])
      end

      -- Force an immediate refresh so the now-stale pin actually
      -- disappears right away, rather than lingering until the next
      -- natural QUEST_LOG_UPDATE-triggered pass happens to catch it.
      -- Note this only clears the RENDERED node -- the underlying
      -- learned location data in AutoQuestLearnerDB.learned is
      -- untouched, so it's still there next time this quest (or one
      -- like it) comes up.
      if AutoQuestLearnerMap then
        AutoQuestLearnerMap:UpdateNodes()
      end

      AutoQuestLearner.abandon = ""
    else
      if entry[4] == "NEW" then
        AutoQuestLearner:Debug("New Quest: " .. entry[1] .. " (" .. entry[2] .. ")")
      else
        AutoQuestLearner:Debug("Update Quest: " .. entry[1] .. " (" .. entry[2] .. ")")
      end

      if AutoQuestLearnerMap then
        AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", entry[1])
      end

      if AutoQuestLearnerData.quests and AutoQuestLearnerData.quests["loc"] and AutoQuestLearnerData.quests["loc"][entry[2]] then
        if AutoQuestLearnerMap then
          AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", AutoQuestLearnerData.quests["loc"][entry[2]].T)
        end
      end

      local meta = { ["addon"] = "AUTOQUESTLEARNER", ["qlogid"] = entry[3] }
      if AutoQuestLearner.db then
        AutoQuestLearner.db:SearchQuestID(entry[2], meta)
      end
    end

    AutoQuestLearner.queue[id] = nil

    for id, entry in pairs(this.queue) do
      return
    end
  end

  if tsize(this.queue) == 0 then
    this.updateQuestLog = true
    this.updateQuestGivers = true
  end
end)

-- Tracks which quests have already had their objective ("cloud of
-- dots") nodes cleared after becoming complete, so DeleteObjectiveNodes
-- only actually needs to run once per completion rather than on every
-- poll while the quest sits complete-but-not-yet-turned-in. Reset when
-- a quest leaves the log (turned in/abandoned) so re-accepting it (or a
-- repeatable version of it) can be culled again next time.
AutoQuestLearner.objectiveNodesCulled = AutoQuestLearner.objectiveNodesCulled or {}

local questlog_flip, questlog_flop = {}, {}
function AutoQuestLearner:UpdateQuestlog()
  AutoQuestLearner.questlog_tmp = AutoQuestLearner.questlog_tmp or questlog_flip

  local _, numQuests = GetNumQuestLogEntries()
  local found = 0
  local change = nil

  for qlogid=1,40 do
    local title, _, _, header, _, complete, daily = AutoQuestLearner_GetQuestLogTitle(qlogid)
    local objectives = GetNumQuestLeaderBoards(qlogid)
    local watched, questid, state

    if title and not header then
      questid = AutoQuestLearner.db and AutoQuestLearner.db:GetQuestIDs(qlogid)
      questid = questid and questid[1] and tonumber(questid[1]) or title
      watched = IsQuestWatched(qlogid)
      state = watched and "track" or ""

      -- Daily quests (and other repeatables Blizzard flags this way)
      -- can be completed over and over -- once we've seen a quest
      -- flagged daily while it was actually in the log, permanently
      -- remember that, so completion history for it never gets treated
      -- as "done forever" for available-quest-suggestion purposes (see
      -- AutoQuestLearner_IsQuestCompleted).
      if daily and questid then
        AutoQuestLearnerDB.repeatable = AutoQuestLearnerDB.repeatable or {}
        if not AutoQuestLearnerDB.repeatable[questid] then
          AutoQuestLearnerDB.repeatable[questid] = true
          if title then
            AutoQuestLearnerDB.repeatable[title] = true
          end
        end
      end

      -- BUGFIX: same cross-check as FindClosestQuestDestination() in
      -- AutoQuestLearner.lua -- Blizzard's isComplete flag (`complete`
      -- above) can lag behind the leaderboard actually reporting every
      -- objective done, which left completed quests' objective nodes
      -- never getting culled from the map (matching the arrow bug: both
      -- point at the same root cause, this client's isComplete flag
      -- just isn't trustworthy on its own).
      if not complete and objectives and objectives > 0 then
        local allDone = true
        for i = 1, objectives do
          local _, _, objDone = GetQuestLogLeaderBoard(i, qlogid)
          if not objDone then
            allDone = false
            break
          end
        end
        if allDone then
          complete = true
        end
      end

      if complete and questid and not AutoQuestLearner.objectiveNodesCulled[questid] then
        AutoQuestLearner.objectiveNodesCulled[questid] = true
        if AutoQuestLearnerMap and AutoQuestLearnerMap.DeleteObjectiveNodes then
          AutoQuestLearnerMap:DeleteObjectiveNodes("AUTOQUESTLEARNER", questid, title)
          AutoQuestLearnerMap:UpdateNodes()
        end
      elseif not complete and questid then
        -- Not (or no longer) complete -- allow a future completion of
        -- this same quest instance to cull again.
        AutoQuestLearner.objectiveNodesCulled[questid] = nil
      end

      if objectives then
        for i=1, objectives, 1 do
          local text, otype, done = GetQuestLogLeaderBoard(i, qlogid)
          state = state .. i .. (done and "done" or "todo")

          -- Per-sub-objective node cleanup: the moment one specific
          -- objective within a multi-target quest completes (e.g.
          -- "Prairie Wolf Heart: 1/1" out of 4 different required
          -- items), remove just that target's spawn cluster -- not the
          -- whole quest's, since the other 3 might still be in
          -- progress. AutoQuestLearner.objCulled tracks which
          -- objIndexes have already been culled this session so this
          -- doesn't repeat the (cheap, but pointless) work every poll.
          if done and AutoQuestLearnerMap and AutoQuestLearnerMap.DeleteObjectiveNodesByName then
            AutoQuestLearner.objCulled = AutoQuestLearner.objCulled or {}
            AutoQuestLearner.objCulled[questid] = AutoQuestLearner.objCulled[questid] or {}
            if not AutoQuestLearner.objCulled[questid][i] then
              AutoQuestLearner.objCulled[questid][i] = true
              local objLabel = text and string.match(text, "^(.-)%s*:?%s*%d") or text
              if objLabel then
                AutoQuestLearnerMap:DeleteObjectiveNodesByName("AUTOQUESTLEARNER", questid, objLabel, title)
                AutoQuestLearnerMap:UpdateNodes()
              end
            end
          end

          -- Objective-location learning: this is separate from the
          -- pickup/turn-in learning in AutoQuestLearner.lua. It watches
          -- for the player's kill/collect/interact progress on this
          -- objective increasing between polls, and if it has, records
          -- the player's current position as a known spawn point for
          -- whatever the objective requires. This is what lets the
          -- addon build its own "spawn points" data (like pfDB's obj.U/
          -- obj.O clusters) purely from observed play, with no
          -- dependency on any external database.
          if AutoQuestLearner_RecordObjectiveProgress and text then
            AutoQuestLearner.objProgress = AutoQuestLearner.objProgress or {}
            AutoQuestLearner.objProgress[questid] = AutoQuestLearner.objProgress[questid] or {}

            local cur = tonumber(string.match(text, "(%d+)%s*/%s*%d+"))
            local prevCur = AutoQuestLearner.objProgress[questid][i]
            local progressed = false

            if cur and prevCur and cur > prevCur then
              progressed = true
            elseif cur == nil and done and not AutoQuestLearner.objProgress[questid][tostring(i) .. "_done"] then
              -- Non-numeric objective (e.g. "Speak to X", "Use item")
              -- that just flipped to complete.
              progressed = true
              AutoQuestLearner.objProgress[questid][tostring(i) .. "_done"] = true
            end

            if cur then AutoQuestLearner.objProgress[questid][i] = cur end

            if progressed then
              local objLabel = string.match(text, "^(.-)%s*:?%s*%d") or text
              AutoQuestLearner_RecordObjectiveProgress(questid, i, objLabel)
            end
          end
        end
      end

      if not AutoQuestLearner.questlog[questid] then
        if AutoQuestLearner_FlickerDebug and AutoQuestLearner_FlickerDebug.active then
          local c = AutoQuestLearner_FlickerDebug.counts
          local key = "ChangeReason_NEW:" .. tostring(title)
          c[key] = (c[key] or 0) + 1
        end
        table.insert(AutoQuestLearner.queue, { title, questid, qlogid, "NEW" })
        AutoQuestLearner.questlog_tmp[questid] = {
          title = title,
          qlogid = qlogid,
          state = state,
        }
        change = true

        -- Freshly picked up -- immediately plot every known static
        -- spawn point for this quest's objectives (pfQuest-style
        -- cluster), rather than waiting for the player to personally
        -- stumble on each one over time. Wrapped in pcall: this runs
        -- inside the same per-quest scan loop that also drives
        -- autolearning's own quest-detection bookkeeping just above --
        -- an unprotected error here would abort the rest of this
        -- function (including the questlog_tmp -> questlog swap later
        -- on), which would break autolearning entirely, not just the
        -- map drawing. A failure here should never be able to take
        -- down anything else.
        if AutoQuestLearner_DrawStaticObjectiveClusters then
          local ok, err = pcall(AutoQuestLearner_DrawStaticObjectiveClusters, questid, title)
          if not ok and AutoQuestLearner.Debug then
            AutoQuestLearner:Debug("DrawStaticObjectiveClusters error: " .. tostring(err))
          end
        end
      elseif AutoQuestLearner.questlog[questid].qlogid ~= qlogid then
        if AutoQuestLearner_FlickerDebug and AutoQuestLearner_FlickerDebug.active then
          local c = AutoQuestLearner_FlickerDebug.counts
          local key = "ChangeReason_QLOGID:" .. tostring(title)
          c[key] = (c[key] or 0) + 1
        end
        table.insert(AutoQuestLearner.queue, { title, questid, qlogid, "RELOAD" })
        AutoQuestLearner.questlog_tmp[questid] = AutoQuestLearner.questlog[questid]
        AutoQuestLearner.questlog_tmp[questid].qlogid = qlogid
        AutoQuestLearner.questlog_tmp[questid].state = state
        change = true
      elseif AutoQuestLearner.questlog[questid].state ~= state then
        if AutoQuestLearner_FlickerDebug and AutoQuestLearner_FlickerDebug.active then
          local c = AutoQuestLearner_FlickerDebug.counts
          local key = "ChangeReason_STATE:" .. tostring(title)
          c[key] = (c[key] or 0) + 1
          local key2 = "ChangeReason_STATE_detail:" .. tostring(title) .. " OLD=[" .. tostring(AutoQuestLearner.questlog[questid].state) .. "] NEW=[" .. tostring(state) .. "]"
          c[key2] = (c[key2] or 0) + 1
        end
        table.insert(AutoQuestLearner.queue, { title, questid, qlogid, "RELOAD" })
        AutoQuestLearner.questlog_tmp[questid] = AutoQuestLearner.questlog[questid]
        AutoQuestLearner.questlog_tmp[questid].qlogid = qlogid
        AutoQuestLearner.questlog_tmp[questid].state = state
        change = true
      else
        AutoQuestLearner.questlog_tmp[questid] = AutoQuestLearner.questlog[questid]
      end

      found = found + 1
      if found >= numQuests then
        break
      end
    end
  end

  for questid, data in pairs(AutoQuestLearner.questlog) do
    if not AutoQuestLearner.questlog_tmp[questid] then
      table.insert(AutoQuestLearner.queue, { data.title, questid, nil, "REMOVE" })
      change = true
    end
  end

  AutoQuestLearner.questlog = AutoQuestLearner.questlog_tmp

  if AutoQuestLearner.questlog_tmp == questlog_flip then
    AutoQuestLearner.questlog_tmp = questlog_flop
  else
    AutoQuestLearner.questlog_tmp = questlog_flip
  end

  for k, v in pairs(AutoQuestLearner.questlog_tmp) do
    AutoQuestLearner.questlog_tmp[k] = nil
  end

  return change
end

function AutoQuestLearner:ResetAll()
  if AutoQuestLearnerMap then
    AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER")
  end
  AutoQuestLearner.questlog = {}
  AutoQuestLearner.updateQuestLog = true
  AutoQuestLearner.updateQuestGivers = true
end

StaticPopupDialogs["AUTOQUESTLEARNER_URLCOPY"] = {
  text = "|cff00ff00AutoQuestLearner|r Online Search",
  button1 = "Close",
  hasEditBox = 1,
  hasWideEditBox = 1,
  timeout = 0,
  exclusive = 1,
  whileDead = 1,
  hideOnEscape = 1,
  OnShow = function()
    local editBox = _G[this:GetName().."WideEditBox"]
    editBox:SetText(StaticPopupDialogs["AUTOQUESTLEARNER_URLCOPY"].data)
    editBox:HighlightText()
  end,
  OnHide = function()
    _G[this:GetName().."WideEditBox"]:SetText("")
  end,
  EditBoxOnEnterPressed = function()
    this:GetParent():Hide()
  end,
  EditBoxOnEscapePressed = function()
    this:GetParent():Hide()
  end,
  EditBoxOnTextChanged = function()
    this:SetText(StaticPopupDialogs["AUTOQUESTLEARNER_URLCOPY"].data)
    this:HighlightText()
  end,
}

function AutoQuestLearner:AddQuestLogIntegration()
  if AutoQuestLearnerDB.config.questlogbuttons == "0" then return end

  local dockFrame = QuestLogDetailScrollChildFrame
  local dockTitle = QuestLogDescriptionTitle

  if dockTitle then
    dockTitle:SetHeight(dockTitle:GetHeight() + 30)
    dockTitle:SetJustifyV("BOTTOM")
  end

  AutoQuestLearner.buttonOnline = AutoQuestLearner.buttonOnline or CreateFrame("Button", "AutoQuestLearnerOnline", dockFrame)
  AutoQuestLearner.buttonOnline:SetWidth(18)
  AutoQuestLearner.buttonOnline:SetHeight(15)
  AutoQuestLearner.buttonOnline:SetPoint("TOPRIGHT", dockFrame, "TOPRIGHT", -12, -10)
  AutoQuestLearner.buttonOnline:SetScript("OnClick", function()
    StaticPopupDialogs["AUTOQUESTLEARNER_URLCOPY"].data = AutoQuestLearner.dburl .. (this:GetID() or 0)
    local dialog = StaticPopup_Show("AUTOQUESTLEARNER_URLCOPY")
    if dialog then
      _G[dialog:GetName().."Button1"]:ClearAllPoints()
      _G[dialog:GetName().."Button1"]:SetPoint("BOTTOM", dialog, "BOTTOM", 0, 16)
      _G[dialog:GetName().."WideEditBox"]:SetScript('OnTextChanged', StaticPopup_EditBoxOnTextChanged)
      dialog:SetWidth(420)
    end
  end)

  AutoQuestLearner.buttonOnline.txt = AutoQuestLearner.buttonOnline:CreateFontString("AutoQuestLearnerIDButton", "HIGH", "GameFontWhite")
  AutoQuestLearner.buttonOnline.txt:SetAllPoints(AutoQuestLearner.buttonOnline)
  AutoQuestLearner.buttonOnline.txt:SetJustifyH("RIGHT")
  AutoQuestLearner.buttonOnline.txt:SetText("|cff000000[|cffaa2222?|cff000000]")

  AutoQuestLearner.buttonShow = AutoQuestLearner.buttonShow or CreateFrame("Button", "AutoQuestLearnerShow", dockFrame, "UIPanelButtonTemplate")
  AutoQuestLearner.buttonShow:SetWidth(70)
  AutoQuestLearner.buttonShow:SetHeight(20)
  AutoQuestLearner.buttonShow:SetText("Show")
  AutoQuestLearner.buttonShow:SetPoint("TOP", dockTitle, "TOP", -110, 0)
  AutoQuestLearner.buttonShow:SetScript("OnClick", function()
    local questIndex = GetQuestLogSelection()
    local questids = AutoQuestLearner.db and AutoQuestLearner.db:GetQuestIDs(questIndex)
    local title, _, _, header, _, complete = AutoQuestLearner_GetQuestLogTitle(questIndex)
    local id = questids and questids[1] and tonumber(questids[1])
    if header or not id then return end

    local maps, meta = {}, { ["addon"] = "AUTOQUESTLEARNER", ["qlogid"] = questIndex }
    if AutoQuestLearner.db then
      maps = AutoQuestLearner.db:SearchQuestID(id, meta, maps)
    end
    if AutoQuestLearner.db then
      local bestMap = AutoQuestLearner.db:GetBestMap(maps)
      if bestMap and AutoQuestLearnerMap then
        AutoQuestLearnerMap:ShowMapID(bestMap)
      end
    end
  end)

  AutoQuestLearner.buttonHide = AutoQuestLearner.buttonHide or CreateFrame("Button", "AutoQuestLearnerHide", dockFrame, "UIPanelButtonTemplate")
  AutoQuestLearner.buttonHide:SetWidth(70)
  AutoQuestLearner.buttonHide:SetHeight(20)
  AutoQuestLearner.buttonHide:SetText("Hide")
  AutoQuestLearner.buttonHide:SetPoint("TOP", dockTitle, "TOP", -37, 0)
  AutoQuestLearner.buttonHide:SetScript("OnClick", function()
    local questIndex = GetQuestLogSelection()
    local title, _, _, header, _, complete = AutoQuestLearner_GetQuestLogTitle(questIndex)
    if header then return end

    if AutoQuestLearnerMap then
      AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", title)
    end
  end)

  AutoQuestLearner.buttonClean = AutoQuestLearner.buttonClean or CreateFrame("Button", "AutoQuestLearnerClean", dockFrame, "UIPanelButtonTemplate")
  AutoQuestLearner.buttonClean:SetWidth(70)
  AutoQuestLearner.buttonClean:SetHeight(20)
  AutoQuestLearner.buttonClean:SetText("Clean")
  AutoQuestLearner.buttonClean:SetPoint("TOP", dockTitle, "TOP", 37, 0)
  AutoQuestLearner.buttonClean:SetScript("OnClick", function()
    if AutoQuestLearnerMap then
      AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER")
    end
  end)

  AutoQuestLearner.buttonReset = AutoQuestLearner.buttonReset or CreateFrame("Button", "AutoQuestLearnerReset", dockFrame, "UIPanelButtonTemplate")
  AutoQuestLearner.buttonReset:SetWidth(70)
  AutoQuestLearner.buttonReset:SetHeight(20)
  AutoQuestLearner.buttonReset:SetText("Reset")
  AutoQuestLearner.buttonReset:SetPoint("TOP", dockTitle, "TOP", 110, 0)
  AutoQuestLearner.buttonReset:SetScript("OnClick", function()
    AutoQuestLearner:ResetAll()
  end)
end

function AutoQuestLearner:AddWorldMapIntegration()
  if AutoQuestLearnerDB.config.worldmapmenu == "0" then return end

  AutoQuestLearner.mapButton = CreateFrame("Frame", "AutoQuestLearnerMapDropdown", WorldMapButton, "UIDropDownMenuTemplate")
  AutoQuestLearner.mapButton:ClearAllPoints()
  AutoQuestLearner.mapButton:SetPoint("TOPRIGHT", 0, -10)
  AutoQuestLearner.mapButton:SetScript("OnShow", function()
    AutoQuestLearner.mapButton.current = tonumber(AutoQuestLearnerDB.config.trackingmethod) or 1
    AutoQuestLearner.mapButton:UpdateMenu()
  end)

  AutoQuestLearner.mapButton.point = "TOPLEFT"
  AutoQuestLearner.mapButton.relativePoint = "BOTTOMLEFT"

  function AutoQuestLearner.mapButton:UpdateMenu()
    local function CreateEntries()
      local info = {}
      info.text = "All Quests"
      info.checked = false
      info.func = function()
        UIDropDownMenu_SetSelectedID(AutoQuestLearner.mapButton, this:GetID(), 0)
        AutoQuestLearnerDB.config.trackingmethod = this:GetID()
        AutoQuestLearner:ResetAll()
      end
      UIDropDownMenu_AddButton(info)

      local info = {}
      info.text = "Tracked Quests"
      info.checked = false
      info.func = function()
        UIDropDownMenu_SetSelectedID(AutoQuestLearner.mapButton, this:GetID(), 0)
        AutoQuestLearnerDB.config.trackingmethod = this:GetID()
        AutoQuestLearner:ResetAll()
      end
      UIDropDownMenu_AddButton(info)

      local info = {}
      info.text = "Manual Selection"
      info.checked = false
      info.func = function()
        UIDropDownMenu_SetSelectedID(AutoQuestLearner.mapButton, this:GetID(), 0)
        AutoQuestLearnerDB.config.trackingmethod = this:GetID()
        AutoQuestLearner:ResetAll()
      end
      UIDropDownMenu_AddButton(info)

      local info = {}
      info.text = "Hide Quests"
      info.checked = false
      info.func = function()
        UIDropDownMenu_SetSelectedID(AutoQuestLearner.mapButton, this:GetID(), 0)
        AutoQuestLearnerDB.config.trackingmethod = this:GetID()
        AutoQuestLearner:ResetAll()
      end
      UIDropDownMenu_AddButton(info)
    end

    UIDropDownMenu_Initialize(AutoQuestLearner.mapButton, CreateEntries)
    UIDropDownMenu_SetWidth(AutoQuestLearner.mapButton, 120)
    UIDropDownMenu_SetButtonWidth(AutoQuestLearner.mapButton, 125)
    UIDropDownMenu_JustifyText(AutoQuestLearner.mapButton, "RIGHT")
    UIDropDownMenu_SetSelectedID(AutoQuestLearner.mapButton, AutoQuestLearner.mapButton.current)
  end
end

local pfHookRemoveQuestWatch = RemoveQuestWatch
RemoveQuestWatch = function(questIndex)
  local ret = pfHookRemoveQuestWatch(questIndex)

  if questIndex then
    local title, _, _, header, _, complete = AutoQuestLearner_GetQuestLogTitle(questIndex)
    if AutoQuestLearnerMap then
      AutoQuestLearnerMap:DeleteNode("AUTOQUESTLEARNER", title)
    end
  end

  AutoQuestLearner.updateQuestLog = true
  AutoQuestLearner.updateQuestGivers = true

  return ret
end

local pfHookAddQuestWatch = AddQuestWatch
AddQuestWatch = function(questIndex)
  local ret = pfHookAddQuestWatch(questIndex)
  AutoQuestLearner.updateQuestLog = true
  AutoQuestLearner.updateQuestGivers = true
  return ret
end

local HookAbandonQuest = AbandonQuest
AbandonQuest = function()
  AutoQuestLearner.abandon = GetAbandonQuestName()
  HookAbandonQuest()
end

local function UpdateQuestLevel(button, id)
  local title, level, tag, header = AutoQuestLearner_GetQuestLogTitle(id)
  if header or not title then return end
  button:SetText(" [" .. ( level or "??" ) .. ( tag and "+" or "") .. "] " .. title)
end

local pfHookQuestLog_Update = QuestLog_Update
QuestLog_Update = function()
  pfHookQuestLog_Update()

  if AutoQuestLearnerDB.config.questloglevel == "1" then
    if QuestLogScrollFrame and QuestLogScrollFrame.buttons then
      for i, button in pairs(QuestLogScrollFrame.buttons) do
        UpdateQuestLevel(button, button:GetID())
      end
    elseif QUESTS_DISPLAYED then
      for i=1, QUESTS_DISPLAYED, 1 do
        UpdateQuestLevel(_G["QuestLogTitle"..i], i + FauxScrollFrame_GetOffset(QuestLogListScrollFrame))
      end
    end
  end

  if AutoQuestLearnerDB.config.questlogbuttons == "1" then
    local questids = AutoQuestLearner.db and AutoQuestLearner.db:GetQuestIDs(GetQuestLogSelection())
    if questids and questids[1] and tonumber(questids[1]) and AutoQuestLearner.questlog[questids[1]] then
      AutoQuestLearner.buttonOnline:SetID(questids[1])
      AutoQuestLearner.buttonOnline:Show()
      AutoQuestLearner.buttonShow:Enable()
      AutoQuestLearner.buttonHide:Enable()
    else
      AutoQuestLearner.buttonOnline:Hide()
      AutoQuestLearner.buttonShow:Disable()
      AutoQuestLearner.buttonHide:Disable()
    end
  end
end

if QuestLogScrollFrame then
  QuestLogScrollFrame.update = QuestLog_Update
end

local pfHookQuestLogTitleButton_OnClick = QuestLogTitleButton_OnClick
QuestLogTitleButton_OnClick = function(self, button)
  pfHookQuestLogTitleButton_OnClick(self, button)
  QuestLog_Update()
end
