-- ========================================================================= --
--                              SylingTracker                                --
--           https://www.curseforge.com/wow/addons/sylingtracker             --
--                                                                           --
--                               Repository:                                 --
--                   https://github.com/Skamer/SylingTracker                 --
--                                                                           --
-- ========================================================================= --
Syling                  "SylingTracker.Achievements"                         ""
-- ========================================================================= --
import                              "SLT"
-- ========================================================================= --
_Active                             = false 
-- ========================================================================= --
RegisterContentType = API.RegisterContentType
RegisterModel       = API.RegisterModel
-- ========================================================================= --
_AchievementModel = RegisterModel(AchievementModel, "achievements-data")
-- ========================================================================= --
HasAchievements                     = Utils.Achievement.HasAchievements
GetAchievementInfo                  = GetAchievementInfo
GetAchievementNumCriteria           = GetAchievementNumCriteria
IsAchievementEligible               = IsAchievementEligible
-- ========================================================================= --
-- Register the achievements content type
-- ========================================================================= --
RegisterContentType({
  ID = "achievements",
  DisplayName = "Achievements",
  Description = "Display the achievements tracked",
  DefaultOrder = 10,
  DefaultModel = _AchievementModel,
  DefaultViewClass = AchievementsContentView,
  Events = { "TRACKED_ACHIEVEMENT_LIST_CHANGED", "PLAYER_ENTERING_WORLD" },
  Status = function() return HasAchievements() end
})
-- ========================================================================= --
_AchievementsCache = {}
_ReadyForFetching  = false
-- ========================================================================= --
__ActiveOnEvents__ "PLAYER_ENTERING_WORLD" "TRACKED_ACHIEVEMENT_LIST_CHANGED"
function ActivateOn(self, event)
  if event == "PLAYER_ENTERING_WORLD" then 
    _ReadyForFetching = true 
  end

  return HasAchievements()
end
-- ========================================================================= --
function OnActive(self)
  -- NOTE: This seems the first time the player enters in the game, the achievements
  -- details may be incorrect and missing. The workaround is to wait the 
  -- PLAYER_ENTERING_WORLD event which will set _ReadyForFetching to true for 
  -- saying it's ready for fetching the information.
  if _ReadyForFetching then 
    self:LoadAchievements()
  end
end

function OnInactive(self)
  _AchievementModel:ClearData()

  -- Clear the cache 
  wipe(_AchievementsCache)
end
-- ========================================================================= --
__SystemEvent__()
function TRACKED_ACHIEVEMENT_UPDATE(achievementID)
  -- NOTE: We need to check the achievement is tracked for avoiding to add 
  -- untracked achievements. 
  if _AchievementsCache[achievementID] then 
    _M:UpdateAchievement(achievementID)
    _AchievementModel:Flush()
  end
end

__SystemEvent__()
function PLAYER_ENTERING_WORLD()
  _ReadyForFetching = true
  
  -- The information are now ready to be fetched, so we can load achievements
  _M:LoadAchievements()
end

__SystemEvent__()
function TRACKED_ACHIEVEMENT_LIST_CHANGED(achievementID, isAdded)
  if not _ReadyForFetching then 
    return 
  end

  -- NOTE: When an achievement has failed, TRACKED_ACHIEVEMENT_LIST_CHANGED is triggered with
  -- achievementID and isAdded having a nil value
  if achievementID then
    if isAdded then 
      _AchievementsCache[achievementID] = true
      _M:UpdateAchievement(achievementID)
    else
      _AchievementModel:RemoveAchievementData(achievementID)
      _AchievementsCache[achievementID] = nil 
    end

    _AchievementModel:Flush() 
  else
    -- NOTE: When an anchievement has failed, achievementID and isAdded have a nil value.
    -- We must update all for the achievement eligibility is correctly updated.
    -- Infortunnaly we don't know which achievement has an eligibility change.
    _M:UpdateAllAchievements()
  end
end


function LoadAchievements(self)
  local trackedAchievements = { GetTrackedAchievements() }
  for i = 1, #trackedAchievements do 
    local achievementID = trackedAchievements[i]
    TRACKED_ACHIEVEMENT_LIST_CHANGED(achievementID, true)
  end
end

function UpdateAllAchievements(self)
  for achievementID in pairs(_AchievementsCache) do 
    self:UpdateAchievement(achievementID)
  end
  
  _AchievementModel:Flush()
end

function UpdateAchievement(self, achievementID)
  local _, name, points, completed, month, day, year, description, flags, icon,
  rewardText, isGuild, wasEarnedByMe, earnedBy = GetAchievementInfo(achievementID)

  local numObjectives = GetAchievementNumCriteria(achievementID)
  local failed = IsAchievementEligible(achievementID)

  local achievementData = {
    achievementID = achievementID,
    title = name, 
    name  = name,
    numObjectives = numObjectives,
    points = points, 
    failed = failed,
    completed = completed,
    month = month, 
    day = day, 
    year = year, 
    description = description,
    flags = flags, 
    icon = icon, 
    rewardText = rewardText,
    isGuild = isGuild,
    wasEarnedByMe = wasEarnedByMe,
    earnedBy = earnedBy
  }

  if numObjectives > 0 then 
    local objectivesData = {}
    for index = 1, numObjectives do 
      local criteriaString, criteriaType, criteriaCompleted, quantity, 
      totalQuantity, name, flags, assetID, quantityString, criteriaID, 
      eligible, duration, elapsed = GetAchievementCriteriaInfo(achievementID, index)

      local data = {
        text = criteriaString,
        failed = not eligible,
        isCompleted = criteriaCompleted
      }

      if bit.band(flags, EVALUATION_TREE_FLAG_PROGRESS_BAR) == EVALUATION_TREE_FLAG_PROGRESS_BAR then 
        data.text = description
        data.hasProgressBar = true
        data.progress = quantity
        data.minProgress = 0
        data.maxProgress = totalQuantity 
        data.progressText = format("%i / %i", quantity, totalQuantity)
      end 

      objectivesData[index] = data
    end

    achievementData.objectives = objectivesData
  end

  _AchievementModel:AddAchievementData(achievementID, achievementData)
end
-- ========================================================================= --
-- Debug Utils Tools
-- ========================================================================= --
if ViragDevTool_AddData then 
  ViragDevTool_AddData(_AchievementModel, "SLT Achievement Model")
end