public static LeagueTeam GetLeagueTeam(this Player player, Gameweek gameweek)
 {
     var league = player.Team.Leagues.FirstOrDefault(x => x.ID == gameweek.League.ID);
     var leaugeTeam = GetLeagueTeam(player, league);
     return leaugeTeam;
 }
        private League GenerateLeague(JObject leagueDataJson, JObject fixturesJson, ref List<Team> teams)
        {
            var league = new League
            {
                ID = "PL",
                Name = "Premier League 16/17",
                Year = 2016,
            };
            var leagueTeams = new List<LeagueTeam>();
            var gameweeks = new List<Gameweek>();
            var positions = new Dictionary<int, PlayerPosition>();


            // Get playing positions
            var typeInfo = leagueDataJson?.Property("element_types")?.Value?.ToObject<JArray>();
            if (typeInfo != null)
            {
                foreach (var type in typeInfo.Select(x => x.ToObject<JObject>()).Where(x => x != null))
                {
                    PlayerPosition pos;
                    var posID = type.GetPropertyValue<int>("id");
                    var posShortName = type.GetPropertyValue<string>("singular_name_short");
                    switch (posShortName)
                    {
                        case "GKP":
                            pos = PlayerPosition.Goalkeeper;
                            break;

                        case "DEF":
                            pos = PlayerPosition.Defender;
                            break;

                        case "MID":
                            pos = PlayerPosition.Midfielder;
                            break;

                        case "FWD":
                            pos = PlayerPosition.Forward;
                            break;

                        default:
                            continue;
                    }
                    positions[posID] = pos;
                }
            }
            


            // Get teams
            var eiwTeams = leagueDataJson?.Property("teams")?.Value?.ToObject<JArray>();
            if (eiwTeams != null)
            {
                foreach (var eiwTeam in eiwTeams)
                {
                    var t = eiwTeam?.ToObjectOrDefault<JObject>();
                    if (t == null)
                        continue;
                    var teamID = t.GetPropertyValue<string>("id");
                    if (string.IsNullOrWhiteSpace(teamID))
                        throw new FormatException("Invalid TeamID");

                    var team = teams.FirstOrDefault(x => x.ID == teamID);
                    if (team == null)
                    {
                        team = new Team
                        {
                            ID = teamID,
                            Name = t.GetPropertyValue<string>("name"),
                            ShortName = t.GetPropertyValue<string>("short_name"),

                            // todo: implement
                            //Statistics = 
                            //Rating = new Rating
                            //{
                            //    // todo: normalize to a 1-10 rating
                            //    //Value = (t.GetPropertyValue<int>("strength_overall_home") +
                            //    //        t.GetPropertyValue<int>("strength_overall_away")) / 2
                            //}
                        };
                        teams.Add(team);

                        var club = _ukClubs?.FirstOrDefault(x => x.MatchName(team.Name));
                        if (club != null)
                        {
                            team.Aliases = club.Aliases;
                        }
                    }
                
                    var leagueTeam = new LeagueTeam(league, team);
                    leagueTeams.Add(leagueTeam);
                    team.Leagues = team.Leagues.Append(league);
                }
            }




            // Get players
            var elements = leagueDataJson?.Property("elements")?.Value?.ToObject<JArray>();
            if (elements != null)
            {
                foreach (var playerData in elements)
                {
                    var p = playerData?.ToObjectOrDefault<JObject>();
                    if (p == null)
                        continue;

                    var posID = p.GetPropertyValue<int>("element_type");
                    var pos = positions[posID];
                    
                    // todo: load and update player (statitics), if player plays in multiple leagues

                    var player                          = new Player();
                    player.ID                           = p.GetPropertyValue<string>("id");
                    player.FirstName                    = p.GetPropertyValue<string>("first_name");
                    player.LastName                     = p.GetPropertyValue<string>("second_name");
                    player.DisplayName                  = p.GetPropertyValue<string>("web_name");
                    
                    var currentPrice = p.GetPropertyValue<double>("now_cost") / 10;
                    var costChangeSinceStart = p.GetPropertyValue<double>("cost_change_start") / 10;

                    object indexInfo = new PremierLeagueFantasyIndex
                    {
                        EaIndex = new Assignable<double>(p.GetPropertyValue<double>("ea_index")),
                        Influence = new Assignable<double>(p.GetPropertyValue<double>("influence")),
                        Creativity = new Assignable<double>(p.GetPropertyValue<double>("creativity")),
                        Threat = new Assignable<double>(p.GetPropertyValue<double>("threat")),
                    };

                    player.Fantasy                      = new FantasyPlayer
                    {
                        Position                        = pos,
                        CurrentPrice                    = currentPrice,
                        OriginalPrice                   = currentPrice + costChangeSinceStart,
                        Unavailable                     = p.GetPropertyValue<string>("status") != "a",
                        ChanceOfPlayingNextFixture      = p.GetPropertyValue<double>("chance_of_playing_this_round", -1) / 100,
                        News                            = p.GetPropertyValue<string>("news"),
                        OwnagePercent                   = p.GetPropertyValue<double>("selected_by_percent", -1),
                        TransfersDetailsForSeason       = new TransferDetails
                        {
                            TransfersIn                 = p.GetPropertyValue<int>("transfers_in"),
                            TransfersOut                = p.GetPropertyValue<int>("transfers_out"),
                        },
                        TransfersDetailsForGW           = new TransferDetails
                        {
                            TransfersIn                 = p.GetPropertyValue<int>("transfers_in_event"),
                            TransfersOut                = p.GetPropertyValue<int>("transfers_out_event"),
                        },
                        IndexInfo = indexInfo,
                    };

                    player.Statistics                   = new PlayerStatistics
                    {
                        PlayedMinutes                   = p.GetPropertyValue<int>("minutes"),
                        Goals                           = p.GetPropertyValue<int>("goals_scored"),
                        Assists                         = p.GetPropertyValue<int>("assists"),
                        TimesInDreamteam                = p.GetPropertyValue<int>("dreamteam_count"),
                        YellowCards                     = p.GetPropertyValue<int>("yellow_cards"),
                        RedCards                        = p.GetPropertyValue<int>("red_cards"),
                        BonusPoints                     = p.GetPropertyValue<int>("bonus"),
                        Form                            = p.GetPropertyValue<double>("form"),
                        PenaltiesMissed                 = p.GetPropertyValue<int>("penalties_missed"),
                        PenaltiesSaved                  = p.GetPropertyValue<int>("penalties_scored"),
                        Saves                           = p.GetPropertyValue<int>("saves"),
                        TotalPoints                     = p.GetPropertyValue<int>("total_points"),
                        CleanSheets                     = p.GetPropertyValue<int>("clean_sheets"),
                        PointsPerGame                   = p.GetPropertyValue<double>("points_per_game"),
                        OwnGoals                        = p.GetPropertyValue<int>("own_goals"),


                        // todo:
                        //Appearances = "",
                        //Crosses = 
                        //Offsides = 
                        //PenaltiesScored = 
                        //Substitutions = 
                        //Shots = 
                    };

                    var teamID = p.GetPropertyValue<string>("team");
                    var team = teams.SingleOrDefault(x => x.ID == teamID);
                    if (team != null)
                    {
                        player.Team = team;
                        team.Players = team.Players.Append(player);
                    }
                    else
                    {
                        
                    }

                    // Update Team statistics   (todo: will get wrong if player changes team)
                    //team.Statistics.GoalsFor = player.Statistics.Goals;
                }
            }



            // Fixtures
            if (fixturesJson != null)
            {
                var fixtures = fixturesJson.GetPropertyValue<JArray>("fixtures");
                foreach (var f in fixtures)
                {
                    try
                    {
                        var fixture = new Fixture();

                        var obj = f.ToObjectOrDefault<JObject>();
                        fixture.Time = obj.GetPropertyValue<DateTime>("date");

                        var homeTeamName = obj.GetPropertyValue<string>("homeTeamName");
                        var awayTeamName = obj.GetPropertyValue<string>("awayTeamName");
                        var homeTeam = leagueTeams.SingleOrDefault(x => x.Team.Name == homeTeamName || x.Team.MatchName(homeTeamName));
                        var awayTeam = leagueTeams.SingleOrDefault(x => x.Team.Name == awayTeamName || x.Team.MatchName(awayTeamName));
                        fixture.HomeTeam = homeTeam;
                        fixture.AwayTeam = awayTeam;

                        var resultsJson = obj.GetPropertyValue<JObject>("result");
                        var status = obj.GetPropertyValue<string>("status");
                        var finished = status == "FINISHED";
                        var min = obj.GetPropertyValue<int>("minute");      // todo: correct?
                        if (finished && min <= 0)
                            min = 90;
                        var goalsHome = resultsJson?.GetPropertyValue<int>("goalsHomeTeam") ?? 0;
                        var goalsAway = resultsJson?.GetPropertyValue<int>("goalsAwayTeam") ?? 0;
                        fixture.Statistics = new FixtureStatistics
                        {
                            PlayedMinutes = min,
                            GameFinished = finished,
                            Score = FixtureScore.Create(goalsHome, goalsAway),
                        };


                        var matchday = obj.GetPropertyValue<int>("matchday");
                        var gw = gameweeks.SingleOrDefault(x => x.Number == matchday);
                        if (gw == null)
                        {
                            gw = new Gameweek(league)
                            {
                                Number = matchday,
                            };
                            gameweeks.Add(gw);
                        }
                        fixture.Gameweek = gw;
                        gw.Fixtures = gw.Fixtures.Append(fixture);


                        // Update Team statistics
                        if (fixture.HomeTeam != null && fixture.AwayTeam != null)
                        {
                            fixture.HomeTeam.UpdateStatisticsBasedOnFixture(fixture);
                            fixture.AwayTeam.UpdateStatisticsBasedOnFixture(fixture);
                        }
                        else
                        {

                        }
                    }
                    catch (Exception ex)
                    {
                        throw;
                    }
                }
            }
            

            
            league.Teams = leagueTeams.ToArray();
            league.Gameweeks = gameweeks.ToArray();
            return league;
        }
        private SoccerSimulationPlayerResult AnalysePlayerResult(Player player, Gameweek gameweek, SimulationContext context)
        {
            var res = new SoccerSimulationPlayerResult();
            res.Player = player;

            var fixtures = gameweek.Fixtures.Where(x => x.HomeTeam?.Team?.ID == player.Team.ID || x.AwayTeam?.Team?.ID == player.Team.ID);
            foreach (var fixture in fixtures)
            {
                if (!Settings.SimulateFinishedGames)
                {
                    if (fixture.Statistics.GameFinished)
                        continue;
                }

                if (Settings.PlayerAnalysers != null)
                {
                    foreach (var analyser in Settings.PlayerAnalysers)
                    {
                        if (!analyser.Enabled)
                            continue;
                        var rec = analyser.Analyse(player, fixture, context);
                        if (rec != null)
                        {
                            foreach (var r in rec)
                            {
                                if (r == null || r.Type == PlayerRecommendationType.None)
                                    continue;
                                res.AddRecommendation(r);
                            }
                        }
                    }
                }

                if (Settings.TeamAnalysers != null)
                {
                    foreach (var analyser in Settings.TeamAnalysers)
                    {
                        if (!analyser.Enabled)
                            continue;
                        var rec = analyser.Analyse(player, fixture, context);
                        if (rec != null)
                        {
                            foreach (var r in rec)
                            {
                                if (r == null || r.Type == TeamRecommendationType.None)
                                    continue;
                                res.AddRecommendation(r);
                            }
                        }
                    }
                }

                #region OLD
                /*

                // Algorithms
                // todo: give recommendation if is not Forward and tends to score/assist often
                // todo: give recommendation if is defender, but plays as mid and tends to score (points for CS and goals)
                // todo: logical number algorithm
                // todo: remove magic numbers

                var playerTeam = player.GetLeagueTeam(fixture);
                var opposingTeam = player.GetOpposingTeam(fixture);
                var homeTeamAdvantage = player.HasHomeTeamAdvantage(fixture);
                var gameweeksFromLastPlayedGW = gameweek.Number - context.LastPlayedGameweek.Number;

                //var odds = CalculateOdds(fixture);
                var odds = fixture.Odds;
                if (odds != null)
                {
                    var teamBetter = homeTeamAdvantage
                    ? odds.HomeWin < 2
                    : odds.AwayWin < 2;
                    var teamWorse = homeTeamAdvantage
                        ? odds.HomeWin > 3
                        : odds.AwayWin > 3;

                    // todo: better team-vs-team quality check (with weight)
                    if (teamBetter)
                        res.AddRecommendation(RecommendationType.FixtureRatingDiff, 1);
                    else if (teamWorse)
                        res.AddRecommendation(RecommendationType.FixtureRatingDiff, -1);
                }

                // Positives
                if (homeTeamAdvantage)
                    res.AddRecommendation(RecommendationType.HomeTeamAdvantage, 1);

                if (Settings.MinimumFixturesForPlaytimeRecommendationBonus <= 0 ||
                    playerTeam.Statistics.PlayedGames >= Settings.MinimumFixturesForPlaytimeRecommendationBonus)
                {
                    var teamPlayedMinutes = playerTeam.GetPlayedMinutesBeforeFixtureForTeam(fixture);
                    var playerMinutes = player.GetPlayedMinutesBeforeFixtureForPlayer(fixture);
                    if (teamPlayedMinutes > 0)
                    {
                        var playedPercentage = (double)playerMinutes / teamPlayedMinutes;

                        // Positives
                        if (playedPercentage >= (80d / 90))
                            res.AddRecommendation(RecommendationType.PlayerPlaytime, 2);
                        //else if (playedPercentage >= (70d / 90))
                        //    res.AddRecommendation(RecommendationType.PlayerPlaytime, 2);
                        else if (playedPercentage >= (60d / 90))
                            res.AddRecommendation(RecommendationType.PlayerPlaytime, 1);
                        // Negatives
                        else if (playedPercentage <= 0.15)
                            res.AddRecommendation(RecommendationType.PlayerPlaytime, -1);
                    }
                }

                // Form
                if (gameweeksFromLastPlayedGW > 0 &&
                    gameweeksFromLastPlayedGW <= Settings.LengthOfFormWhenSimulating)
                {
                    // Good player form
                    if (Settings.MinimumFixturesForFormRecommendationBonus <= 0 ||
                        playerTeam.Statistics.PlayedGames >= Settings.MinimumFixturesForFormRecommendationBonus)
                    {
                        // Positives
                        if (player.Statistics.Form >= 15)
                            res.AddRecommendation(RecommendationType.PlayerForm, 3);
                        if (player.Statistics.Form >= 10)
                            res.AddRecommendation(RecommendationType.PlayerForm, 2);
                        else if (player.Statistics.Form >= 6)
                            res.AddRecommendation(RecommendationType.PlayerPlaytime, 1);
                    }

                    // Good team form
                    if (Settings.MinimumFixturesForFormRecommendationBonus <= 0 ||
                        playerTeam.Statistics.PlayedGames >= Settings.MinimumFixturesForFormRecommendationBonus)
                    {
                        // Positives
                        if (playerTeam.Statistics.Form >= 15)
                            res.AddRecommendation(RecommendationType.TeamForm, 3);
                        if (playerTeam.Statistics.Form >= 10)
                            res.AddRecommendation(RecommendationType.TeamForm, 2);
                        else if (playerTeam.Statistics.Form >= 6)
                            res.AddRecommendation(RecommendationType.TeamForm, 1);
                    }
                }

                // Negatives
                if (player.Fantasy.ChanceOfPlayingNextFixture >= 0)
                {
                    if (player.Fantasy.ChanceOfPlayingNextFixture <= 0)
                        res.AddRecommendation(RecommendationType.ChanceOfPlaying, -10);
                    else if (player.Fantasy.ChanceOfPlayingNextFixture <= 0.25)
                        res.AddRecommendation(RecommendationType.ChanceOfPlaying, -3);
                    else if (player.Fantasy.ChanceOfPlayingNextFixture <= 0.50)
                        res.AddRecommendation(RecommendationType.ChanceOfPlaying, -2);
                    else if (player.Fantasy.ChanceOfPlayingNextFixture <= 0.75)
                        res.AddRecommendation(RecommendationType.ChanceOfPlaying, -1);
                }
                if (player.Fantasy.Unavailable)
                    res.AddRecommendation(RecommendationType.PlayerUnavailable, -10);
                */

                #endregion

            }

            return res;
        }