/// <summary>
 /// Parses a DataSnapshot of score records into UserScore objects, and filters out records
 /// whose timestamp does not fall between startTS and endTS.
 /// </summary>
 /// <param name="snapshot">DataSnapshot record of user scores.</param>
 /// <param name="startTS">Earliest valid timestamp of a user score to retrieve.</param>
 /// <param name="endTS">Latest valid timestamp of a user score to retrieve.</param>
 /// <returns>List of valid UserScore objects.</returns>
 private List <UserScore> ParseValidUserScoreRecords(
     DataSnapshot snapshot,
     long startTS,
     long endTS)
 {
     return(snapshot.Children
            .Select(scoreRecord => UserScore.CreateScoreFromRecord(scoreRecord))
            .Where(score => score != null && score.Timestamp > startTS && score.Timestamp <= endTS)
            .Reverse()
            .ToList());
 }
        /// <summary>
        /// Callback when a score record is removed from the database.
        /// </summary>
        private void OnScoreRemoved(object sender, ChildChangedEventArgs args)
        {
            if (args.Snapshot == null || !args.Snapshot.Exists)
            {
                return;
            }
            var score = UserScore.CreateScoreFromRecord(args.Snapshot);

            if (score == null)
            {
                return;
            }
            if (TopScores.Contains(score))
            {
                TopScores.Remove(score);
                RefreshScores();
            }
        }
        /// <summary>
        /// Callback when a score record is added with a score high enough for a spot on the
        /// leaderboard.
        /// </summary>
        private void OnScoreAdded(object sender, ChildChangedEventArgs args)
        {
            if (args.Snapshot == null || !args.Snapshot.Exists)
            {
                return;
            }

            var score = UserScore.CreateScoreFromRecord(args.Snapshot);

            if (score == null)
            {
                return;
            }

            // Verify that score is within start/end times, and isn't already in TopScores.
            if (TopScores.Contains(score))
            {
                return;
            }

            if (EndTime > 0 || Interval > 0)
            {
                var EndTimeInternal = EndTime > 0 ?
                                      EndTime :
                                      (DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond);
                var startTime = Interval > 0 ? EndTimeInternal - Interval : 0;
                if (score.Timestamp > EndTimeInternal || score.Timestamp < startTime)
                {
                    return;
                }
            }

            // Don't add if the same user already has a better score.
            // If the same user has a worse score, remove it.
            var existingScore = TopScores.Find(s => s.UserID == score.UserID);

            if (existingScore != null)
            {
                var bestScore = GetBestScore(existingScore, score);
                if (existingScore == bestScore)
                {
                    return;
                }
                TopScores.Remove(existingScore);
            }
            if (TopScores.Any(s => s.UserID == score.UserID))
            {
                return;
            }

            TopScores.Add(score);
            if (LowestFirst)
            {
                TopScores = TopScores.OrderBy(s => s.Score).Take(ScoresToRetrieve).ToList();
            }
            else
            {
                TopScores = TopScores.OrderByDescending(s => s.Score).Take(ScoresToRetrieve).ToList();
            }
            sendUpdateTopScoresEvent = true;
        }