--[[
  TimePlayedClass, RenanMSV @2023

  A script designed to update a leaderboard with
  the top 10 players who most play your game.
  
  Do not change this script. All configurations can be found
  in the Settings script.
]]

local DataStoreService = game:GetService("DataStoreService")

local Config = require(script.Parent.Settings)

local TimePlayedClass = {}
TimePlayedClass.__index = TimePlayedClass
function TimePlayedClass.new()
  local new = {}
  setmetatable(new, TimePlayedClass)
  
  new._dataStoreName = Config.DATA_STORE
  new._dataStoreStatName = Config.NAME_OF_STAT
  new._scoreUpdateDelay = Config.SCORE_UPDATE * 60
  new._boardUpdateDelay = Config.LEADERBOARD_UPDATE * 60
  new._useLeaderstats = Config.USE_LEADERSTATS
  new._nameLeaderstats = Config.NAME_LEADERSTATS
  new._doDebug = Config.DO_DEBUG
  
  new._datastore = nil
  new._scoreBlock = script.Parent.Model.ScoreBlock
  
  new:_init()
  
  return new
end

function TimePlayedClass:_timeToString(_time)
  _time = _time * 60
  local days = math.floor(_time / 86400)
  local hours = math.floor(math.fmod(_time, 86400) / 3600)
  local minutes = math.floor(math.fmod(_time, 3600) / 60)
  return string.format("%02dd : %02dh : %02dm",days,hours,minutes)
end

function TimePlayedClass:_init()
  local suc, err = pcall(function ()
    self._datastore = game:GetService("DataStoreService"):GetOrderedDataStore(self._dataStoreName)
  end)
  if not suc or self._datastore == nil then warn("Failed to load OrderedDataStore. Error:", err) script.Parent:Destroy() end
  
  -- puts leaderstat value in player
  if self._useLeaderstats then
    game:GetService("Players").PlayerAdded:Connect(function (player)
      task.spawn(function ()
        local stat = Instance.new("NumberValue")
        stat.Name = self._nameLeaderstats
        stat.Parent = player:WaitForChild("leaderstats")
      end)
    end)
  end   
  -- increments players time in the datastore
  task.spawn(function ()
    while true do
      task.wait(self._scoreUpdateDelay)
      self:_updateScore()
    end
  end)
  
  -- update leaderboard
  task.spawn(function ()
    self:_updateBoard() -- update once
    while true do
      task.wait(self._boardUpdateDelay)
      self:_updateBoard()
    end
  end)
  
end

function TimePlayedClass:_clearBoard ()
  for _, folder in pairs({self._scoreBlock.SurfaceGui.Names, self._scoreBlock.SurfaceGui.Photos, self._scoreBlock.SurfaceGui.Score}) do
    for _, item in pairs(folder:GetChildren()) do
      item.Visible = false
    end
  end
end

function TimePlayedClass:_updateBoard ()
  if self._doDebug then print("Updating board") end
  local results = nil
  
  local suc, results = pcall(function ()
    return self._datastore:GetSortedAsync(false, 10, 1):GetCurrentPage()
  end)
  
  if not suc or not results then
    if self._doDebug then warn("Failed to retrieve top 10 with most time. Error:", results) end
    return
  end
  
  local sufgui = self._scoreBlock.SurfaceGui
  self._scoreBlock.Credits.Enabled = true
  self._scoreBlock.SurfaceGui.Enabled = #results ~= 0
  self._scoreBlock.NoDataFound.Enabled = #results == 0
  self:_clearBoard()
  for k, v in pairs(results) do
    local userid = tonumber(string.split(v.key, self._dataStoreStatName)[2])
    local name = game:GetService("Players"):GetNameFromUserIdAsync(userid)
    local score = self:_timeToString(v.value)
    self:_onPlayerScoreUpdate(userid, v.value)
    sufgui.Names["Name"..k].Visible = true
    sufgui.Score["Score"..k].Visible = true
    sufgui.Photos["Photo"..k].Visible = true
    sufgui.Names["Name"..k].Text = name
    sufgui.Score["Score"..k].Text = score
    sufgui.Photos["Photo"..k].Image = game:GetService("Players"):GetUserThumbnailAsync(userid, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
  end
  if self._scoreBlock:FindFirstChild("_backside") then self._scoreBlock["_backside"]:Destroy() end
  local temp = self._scoreBlock.SurfaceGui:Clone()
  temp.Parent = self._scoreBlock
  temp.Name = "_backside"
  temp.Face = Enum.NormalId.Back
  if self._doDebug then print("Board updated sucessfully") end
end

function TimePlayedClass:_updateScore ()
  local suc, err = coroutine.resume(coroutine.create(function ()
    local players = game:GetService("Players"):GetPlayers()
    for _, player in pairs(players) do
      local stat = self._dataStoreStatName .. player.UserId
      local newval = self._datastore:IncrementAsync(stat, self._scoreUpdateDelay / 60)
      if self._doDebug then print("Incremented time played stat of", player, stat, "to", newval) end
    end
  end))
  if not suc then warn(err) end
end

function TimePlayedClass:_onPlayerScoreUpdate (userid, minutes)
  -- updates leaderstats if enabled
  if not self._useLeaderstats then return end
  local player = game:GetService("Players"):GetPlayerByUserId(userid)
  if not player or not player:FindFirstChild("leaderstats") then return end
  local leaderstat = player.leaderstats[self._nameLeaderstats]
  leaderstat.Value = tonumber(minutes)
end

TimePlayedClass.new()