/// <summary>
 /// Determines if a game is out of class based on properties of guest and host arguments.
 /// </summary>
 /// <param name="teamA"></param>
 /// <param name="teamB"></param>
 /// <returns></returns>
 private static bool IsGameOutOfClass(Team teamA, Team teamB)
 {
     return (teamA == null) || (teamB == null)
         || (teamA.DistrictID == Globals.Constants.OutOfStateDistrictString)
         || (teamB.DistrictID == Globals.Constants.OutOfStateDistrictString)
         || (teamA.ClassificationName != teamB.ClassificationName)
         || (teamA.DistrictID == Globals.Constants.IndependentString)
         || (teamB.DistrictID == Globals.Constants.IndependentString);
 }
        /// <summary>
        /// Processes the two teams involved in the specified game.
        /// </summary>
        /// <param name="winner"></param>
        /// <param name="loser"></param>
        /// <param name="operation"></param>
        /// <param name="currentGame"></param>
        private static void ProcessTeams(Team winner, Team loser, Operation operation, Game currentGame)
        {
            try
            {
                if ( (winner == null) && (loser == null) )
                {
                    throw new Exception("Neither team could be found in the database.");
                }

                EditDataFromAllGames(winner, loser, operation, currentGame);

                if ( IsGameOutOfClass(winner, loser) )
                {
                    // One of the two teams is out of state, or the two teams are in different classifications,
                    // or one of the teams is an independent.
                    EditDataFromGamesOutOfClassification(winner, loser, operation, currentGame);
                }
                else
                {
                    // Both teams are in state and in the same classification.
                    EditDataFromGamesInClassification(winner, loser, operation, currentGame);

                    if ( currentGame.IsDistrictGame )
                    {
                        EditDataFromDistrictGames(winner, loser, operation, currentGame);
                    }
                    else if ( currentGame.IsPlayoffGame )
                    {
                        EditDataFromPlayoffGames(winner, loser, operation, currentGame);
                    }
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
 /// <summary>
 /// Edits each team's data (wins, losses) from district games.
 /// </summary>
 /// <param name="winner"></param>
 /// <param name="loser"></param>
 /// <param name="operation"></param>
 /// <param name="currentGame"></param>
 private static void EditWinLossDataFromDistrictGames(Team winner, Team loser, Operation operation, Game currentGame)
 {
     try
     {
         winner.DistrictWins = operation(winner.DistrictWins, 1);
         loser.DistrictLosses = operation(loser.DistrictLosses, 1);
     }
     catch ( Exception ex )
     {
         Globals.ShowExceptionMessage(ex);
     }
 }
 /// <summary>
 /// Edits each team's data (wins, losses) from playoff games.
 /// </summary>
 /// <param name="winner"></param>
 /// <param name="loser"></param>
 /// <param name="operation"></param>
 /// <param name="currentGame"></param>
 private static void EditWinLossDataFromPlayoffGames(Team winner, Team loser, Operation operation, Game currentGame)
 {
     try
     {
         if ( _gameType != GameType.ForfeitForRulesViolation )
         {
             winner.PlayoffWins = operation(winner.PlayoffWins, 1);
             loser.PlayoffLosses = operation(loser.PlayoffLosses, 1);
         }
     }
     catch ( Exception ex )
     {
         Globals.ShowExceptionMessage(ex);
     }
 }
        /// <summary>
        /// Edits each team's data (games, points for, points against) from playoff games.
        /// </summary>
        /// <param name="winner"></param>
        /// <param name="loser"></param>
        /// <param name="operation"></param>
        /// <param name="currentGame"></param>
        /// <param name="winnerScore"></param>
        /// <param name="loserScore"></param>
        private static void EditDataFromPlayoffGames(Team winner, Team loser, Operation operation, Game currentGame)
        {
            try
            {
                EditWinLossDataFromPlayoffGames(winner, loser, operation, currentGame);

                if ( _gameType == GameType.NonForfeit )
                {
                    winner.PlayoffGames = operation(winner.PlayoffGames, 1);
                    winner.PlayoffPointsFor = operation(winner.PlayoffPointsFor, currentGame.WinnerScore);
                    winner.PlayoffPointsAgainst = operation(winner.PlayoffPointsAgainst, currentGame.LoserScore);

                    loser.PlayoffGames = operation(loser.PlayoffGames, 1);
                    loser.PlayoffPointsFor = operation(loser.PlayoffPointsFor, currentGame.LoserScore);
                    loser.PlayoffPointsAgainst = operation(loser.PlayoffPointsAgainst, currentGame.WinnerScore);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
        /// <summary>
        /// Edits each team's data (wins, losses, and first level rating points) from all games.
        /// </summary>
        /// <param name="winner"></param>
        /// <param name="loser"></param>
        /// <param name="operation"></param>
        /// <param name="currentGame"></param>
        private static void EditWinLossDataFromAllGames(Team winner, Team loser, Operation operation, Game currentGame)
        {
            try
            {
                if ( winner != null )
                {
                    winner.Wins = operation(winner.Wins, 1);

                    if ( _gameType == GameType.NonForfeit )
                    {
                        winner.RatingPointGames = operation(winner.RatingPointGames, 1);
                        winner.RatingPointsFirstLevel = operation(winner.RatingPointsFirstLevel, currentGame.WinnerRatingPoints);
                        winner.RatingPointsFirstLevelAverage = Arithmetic.Divide(winner.RatingPointsFirstLevel, winner.RatingPointGames);
                    }
                }

                if ( loser != null )
                {
                    loser.Losses = operation(loser.Losses, 1);

                    loser.RatingPointGames = operation(loser.RatingPointGames, 1);
                    // No need to increment/decrement loser.RatingPointsFirstLevel,
                    // for the operation will always be y = x + 0 or y = x - 0.
                    loser.RatingPointsFirstLevelAverage = Arithmetic.Divide(loser.RatingPointsFirstLevel, loser.RatingPointGames);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
        /// <summary>
        /// Edits each team's data (games, adjusted points for, adjusted point against) from all games
        /// against teams outside the state or outside their classification
        /// </summary>
        /// <param name="winner"></param>
        /// <param name="loser"></param>
        /// <param name="operation"></param>
        /// <param name="winnerAdjustedScore"></param>
        /// <param name="loserAdjustedScore"></param>
        private static void EditDataFromGamesOutOfClassification(Team winner, Team loser, Operation operation, Game currentGame)
        {
            try
            {
                if ( _gameType != GameType.ForfeitForFailureToAppear )
                {
                    if ( winner != null )
                    {
                        winner.OutOfClassificationGames = operation(winner.OutOfClassificationGames, 1);
                        winner.OutOfClassificationAdjustedPointsFor
                            = operation(winner.OutOfClassificationAdjustedPointsFor, currentGame.WinnerAdjustedScore);
                        winner.OutOfClassificationAdjustedPointsAgainst
                            = operation(winner.OutOfClassificationAdjustedPointsAgainst, currentGame.LoserAdjustedScore);
                    }

                    if ( loser != null )
                    {
                        loser.OutOfClassificationGames = operation(loser.OutOfClassificationGames, 1);
                        loser.OutOfClassificationAdjustedPointsFor
                            = operation(loser.OutOfClassificationAdjustedPointsFor, currentGame.LoserAdjustedScore);
                        loser.OutOfClassificationAdjustedPointsAgainst
                            = operation(loser.OutOfClassificationAdjustedPointsAgainst, currentGame.WinnerAdjustedScore);
                    }
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
        /// <summary>
        /// Edits each team's data (games, points for, points against) from district games.
        /// </summary>
        /// <param name="winner"></param>
        /// <param name="loser"></param>
        /// <param name="operation"></param>
        /// <param name="currentGame"></param>
        /// <param name="winnerScore"></param>
        /// <param name="loserScore"></param>
        private static void EditDataFromDistrictGames(Team winner, Team loser, Operation operation, Game currentGame)
        {
            try
            {
                EditWinLossDataFromDistrictGames(winner, loser, operation, currentGame);

                if ( _gameType != GameType.ForfeitForFailureToAppear )
                {
                    winner.DistrictGames = operation(winner.DistrictGames, 1);
                    winner.DistrictPointsFor = operation(winner.DistrictPointsFor, currentGame.WinnerScore);
                    winner.DistrictPointsAgainst = operation(winner.DistrictPointsAgainst, currentGame.LoserScore);

                    loser.DistrictGames = operation(loser.DistrictGames, 1);
                    loser.DistrictPointsFor = operation(loser.DistrictPointsFor, currentGame.LoserScore);
                    loser.DistrictPointsAgainst = operation(loser.DistrictPointsAgainst, currentGame.WinnerScore);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
        private void UpdateThirdLevelDataByTeam(Team team, GetTeamScheduleTotals_Result result)
        {
            try
            {
                if ( result.TotalRatingPointsThirdLevel != null )
                {
                    // Rating Points Third Level total and average
                    team.RatingPointsThirdLevel = (decimal)result.TotalRatingPointsThirdLevel;
                    team.RatingPointsThirdLevelAverage = Arithmetic.Divide(team.RatingPointsThirdLevel, team.RatingPointGames);

                    lock ( _dbLock )
                    {
                        DataAccess.DbContext.SaveChanges();
                    }
                }
            }
            catch ( InvalidOperationException ex )
            {
                if ( ex.Message == "Nullable object must have a value." )
                {
                    // Ignore exception.
                }
                else
                {
                    Globals.ShowExceptionMessage(ex, "InvalidOperationException - " + team.Name);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex, "Exception - " + team.Name);
            }
        }
        private void UpdateSecondAndThirdLevelDataByTeam(Team team)
        {
            try
            {
                GetTeamScheduleTotals_Result result = null;

                lock ( _dbLock )
                {
                    result = DataAccess.DbContext.GetTeamScheduleTotals(team.Name).FirstOrDefault();
                }

                if ( result != null )
                {
                    UpdateSecondLevelDataByTeam(team, result);
                    UpdateThirdLevelDataByTeam(team, result);
                    UpdateFinalScoringDataByTeam(team, result);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex);
            }
        }
        private void UpdateFinalScoringDataByTeam(Team team, GetTeamScheduleTotals_Result result)
        {
            try
            {
                if ( result.ScheduleTotalGames != null )
                {
                    Classification classification;

                    lock ( _dbLock )
                    {
                        classification = (from c in DataAccess.DbContext.Classifications
                                                where c.Name == team.ClassificationName
                                                select c)
                                                .FirstOrDefault();
                    }

                    team.FinalOffensiveAverage = Arithmetic.Divide(
                         team.InClassificationAdjustedPointsFor * (decimal)result.ScheduleTotalGames
                         * classification.TotalAdjustedPointsFor * team.InClassificationGames
                         + team.InClassificationGames * (decimal)result.ScheduleTotalAdjustedPointsAgainst
                         * classification.TotalGames * team.OutOfClassificationAdjustedPointsFor,
                         team.InClassificationGames * (decimal)result.ScheduleTotalAdjustedPointsAgainst
                         * classification.TotalGames * team.Games);
                    team.FinalDefensiveAverage = Arithmetic.Divide(
                         team.InClassificationAdjustedPointsAgainst * (decimal)result.ScheduleTotalGames
                         * classification.TotalAdjustedPointsFor * team.InClassificationGames
                         + team.InClassificationGames * (decimal)result.ScheduleTotalAdjustedPointsFor
                         * classification.TotalGames * team.OutOfClassificationAdjustedPointsAgainst,
                         team.InClassificationGames * (decimal)result.ScheduleTotalAdjustedPointsFor
                         * classification.TotalGames * team.Games);
                    team.FinalOffensiveFactor = Arithmetic.Divide(
                         team.InClassificationAdjustedPointsFor * (decimal)result.ScheduleTotalGames,
                         team.InClassificationGames * (decimal)result.ScheduleTotalFinalAdjustedPointsAgainst);
                    team.FinalDefensiveFactor = Arithmetic.Divide(
                         team.InClassificationAdjustedPointsAgainst * (decimal)result.ScheduleTotalGames,
                         team.InClassificationGames * (decimal)result.ScheduleTotalFinalAdjustedPointsFor);
                    team.FinalOffensiveIndex
                        = (team.FinalOffensiveAverage + team.FinalOffensiveFactor * classification.AverageAdjustedPointsFor) / 2;
                    team.FinalDefensiveIndex
                        = (team.FinalDefensiveAverage + team.FinalDefensiveFactor * classification.AverageAdjustedPointsFor) / 2;
                    team.FinalScoringRatio = Arithmetic.Divide(
                        (decimal)team.FinalOffensiveIndex, (decimal)team.FinalOffensiveIndex + (decimal)team.FinalDefensiveIndex);

                    lock ( _dbLock )
                    {
                        DataAccess.DbContext.SaveChanges();
                    }
                }
            }
            catch ( InvalidOperationException ex )
            {
                if ( ex.Message == "Nullable object must have a value." )
                {
                    // Ignore exception.
                }
                else
                {
                    Globals.ShowExceptionMessage(ex, "InvalidOperationException - " + team.Name);
                }
            }
            catch ( Exception ex )
            {
                Globals.ShowExceptionMessage(ex, "Exception - " + team.Name);
            }
        }