public List <StandingsResponse> GetStandings(Leaderboard leaderboard, StandingsRequest request)
        {
            if (leaderboard == null)
            {
                throw new MissingRecordException("The provided leaderboard does not exist.");
            }

            var standings = GatherStandings(leaderboard, request);

            _logger.LogInformation($"{standings?.Count} Standings for Leaderboard: {leaderboard.Token}");

            return(standings);
        }
        protected List <StandingsResponse> GatherStandings(Leaderboard leaderboard, StandingsRequest request)
        {
            var evaluationDataController = new EvaluationDataController(EvaluationDataLogger, ContextFactory, leaderboard.EvaluationDataCategory);

            if (request.MultiplePerActor && request.LeaderboardFilterType == LeaderboardFilterType.Near)
            {
                throw new ArgumentException($"You cannot use the filter type: {nameof(LeaderboardFilterType.Near)} in conjunction with {nameof(request.MultiplePerActor)}.");
            }

            var actors = GetActors(evaluationDataController, leaderboard, request);

            List <StandingsResponse> typeResults;

            switch (leaderboard.LeaderboardType)
            {
            case LeaderboardType.Highest:
                typeResults = EvaluateHighest(evaluationDataController, actors, leaderboard, request);
                break;

            case LeaderboardType.Lowest:
                typeResults = EvaluateLowest(evaluationDataController, actors, leaderboard, request);
                break;

            case LeaderboardType.Cumulative:
                typeResults = EvaluateCumulative(evaluationDataController, actors, leaderboard, request);
                break;

            case LeaderboardType.Count:
                typeResults = EvaluateCount(evaluationDataController, actors, leaderboard, request);
                break;

            case LeaderboardType.Earliest:
                typeResults = EvaluateEarliest(evaluationDataController, actors, leaderboard, request);
                break;

            case LeaderboardType.Latest:
                typeResults = EvaluateLatest(evaluationDataController, actors, leaderboard, request);
                break;

            default:
                return(null);
            }

            var results = FilterResults(typeResults, request.PageLimit, request.PageOffset, request.LeaderboardFilterType, request.ActorId);

            _logger.LogInformation($"{results?.Count} Standings for Leaderboard: {leaderboard.Token}");

            return(results);
        }
        private StandingsRequest CreateLeaderboardStandingsRequest(string token, int gameId, LeaderboardFilterType filterType, int actorId = 0, int limit = 0, int offset = 0)
        {
            var filter = new StandingsRequest
            {
                LeaderboardToken      = token,
                GameId                = gameId,
                LeaderboardFilterType = filterType,
                PageLimit             = limit,
                PageOffset            = offset
            };

            if (actorId != 0)
            {
                filter.ActorId = actorId;
            }

            return(filter);
        }
        protected List <StandingsResponse> EvaluateHighest(EvaluationDataController evaluationDataController, List <Actor> actors, Leaderboard leaderboard, StandingsRequest request)
        {
            List <StandingsResponse> results;

            if (request.MultiplePerActor)
            {
                results = GetAllActorResults(evaluationDataController, actors, leaderboard, request);
            }
            else
            {
                switch (leaderboard.EvaluationDataType)
                {
                case EvaluationDataType.Long:
                    results = actors.Select(a => new { Actor = a, Value = evaluationDataController.TryGetMax(leaderboard.GameId, a.Id, leaderboard.EvaluationDataKey, out var value, leaderboard.EvaluationDataType, request.DateStart, request.DateEnd) ? value : null })
                              .Where(a => a.Value != null)
                              .Select(a => new StandingsResponse
                    {
                        ActorId   = a.Actor.Id,
                        ActorName = a.Actor.Name,
                        Value     = a.Value.Value
                    }).ToList();
                    break;
        protected List <Actor> GetActors(EvaluationDataController evaluationDataController, Leaderboard leaderboard, StandingsRequest request)
        {
            switch (request.LeaderboardFilterType)
            {
            case LeaderboardFilterType.Top:
                break;

            case LeaderboardFilterType.Near:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather those ranked near them");
                }
                var nearActor = ActorController.Get(request.ActorId.Value);
                if (nearActor == null || leaderboard.ActorType != ActorType.Undefined && nearActor.ActorType != leaderboard.ActorType)
                {
                    throw new ArgumentException("The provided ActorId cannot compete on this leaderboard.");
                }
                break;

            case LeaderboardFilterType.Friends:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among friends");
                }
                if (leaderboard.ActorType == ActorType.Group)
                {
                    throw new ArgumentException("This leaderboard cannot filter by friends");
                }
                var friendsActor = ActorController.Get(request.ActorId.Value);
                if (friendsActor == null || friendsActor.ActorType != ActorType.User)
                {
                    throw new ArgumentException("The provided ActorId is not an user.");
                }
                break;

            case LeaderboardFilterType.GroupMembers:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among group members");
                }
                if (leaderboard.ActorType == ActorType.Group)
                {
                    throw new ArgumentException("This leaderboard cannot filter by group members");
                }
                var memberActor = ActorController.Get(request.ActorId.Value);
                if (memberActor == null || memberActor.ActorType != ActorType.Group)
                {
                    throw new ArgumentException("The provided ActorId is not a group.");
                }
                break;

            case LeaderboardFilterType.Alliances:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among group alliances");
                }
                if (leaderboard.ActorType == ActorType.User)
                {
                    throw new ArgumentException("This leaderboard cannot filter by group alliances");
                }
                var allianceActor = ActorController.Get(request.ActorId.Value);
                if (allianceActor == null || allianceActor.ActorType != ActorType.Group)
                {
                    throw new ArgumentException("The provided ActorId is not a group.");
                }
                break;
            }
            // get all valid actors (have evaluationDataKey evaluation data in game gameId)
            var validActors = evaluationDataController.GetGameKeyActors(leaderboard.GameId, leaderboard.EvaluationDataKey, leaderboard.EvaluationDataType, request.DateStart, request.DateEnd);
            var actorIds    = validActors
                              .Where(actorId => actorId != null)
                              .Select(actorId => ActorController.Get(actorId.Value)).Where(a => a != null && (leaderboard.ActorType == ActorType.Undefined || leaderboard.ActorType == a.ActorType)).ToList();

            switch (request.LeaderboardFilterType)
            {
            case LeaderboardFilterType.Top:
                break;

            case LeaderboardFilterType.Near:
                break;

            case LeaderboardFilterType.Friends:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among friends");
                }
                var friends = RelationshipCoreController.GetRelatedActors(request.ActorId.Value, ActorType.User).Select(r => r.Id).ToList();
                friends.Add(request.ActorId.Value);
                actorIds = actorIds.Where(a => friends.Contains(a.Id)).ToList();
                break;

            case LeaderboardFilterType.GroupMembers:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among group members");
                }
                var members = RelationshipCoreController.GetRelatedActors(request.ActorId.Value, ActorType.User).Select(r => r.Id).ToList();
                actorIds = actorIds.Where(a => members.Contains(a.Id)).ToList();
                break;

            case LeaderboardFilterType.Alliances:
                if (!request.ActorId.HasValue)
                {
                    throw new ArgumentException("An ActorId has to be passed in order to gather rankings among group alliances");
                }
                var alliances = RelationshipCoreController.GetRelatedActors(request.ActorId.Value, ActorType.Group).Select(r => r.Id).ToList();
                alliances.Add(request.ActorId.Value);
                actorIds = actorIds.Where(a => alliances.Contains(a.Id)).ToList();
                break;
            }

            _logger.LogDebug($"{actorIds.Count} Actors for Filter: {request.LeaderboardFilterType}, ActorType: {leaderboard.ActorType}, ActorId: {request.ActorId}");

            return(actorIds);
        }