/* public members */

        public static void CreateGame(int userId, string title, string password)
        {
            ValidateGameTitle(title);
            if (password != null)
            {
                ValidateGamePassword(password);
            }

            var context = new BattleGameEntities();
            using (context)
            {
                var user = GetUser(userId, context);

                var game = new Game()
                {
                    Title = title,
                    Password = password,
                    Status = context.Statuses.First(st => st.Value == GameStatusOpen),
                    RedUser = user
                };
                context.Games.Add(game);

                context.SaveChanges();
            }
        }
        public static IEnumerable<ActiveGameModel> GetActiveGames(int userId)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                var user = GetUser(userId, context);
                IEnumerable<ActiveGameModel> activeGameModels;
                if (user.GamesCreated.Any() || user.GamesJoined.Any())
                {
                    var gamesList = context.Games.ToList();

                    activeGameModels =
                                      from game in gamesList
                                      where (game.BlueUser == user || game.RedUser == user) &&
                                      (game.Status.Value == GameStatusInProgress || game.Status.Value == GameStatusFull || game.Status.Value == GameStatusOpen)
                                      select new ActiveGameModel()
                                      {
                                          Id = (int)game.Id,
                                          Creator = game.RedUser.Nickname,
                                          Status = game.Status.Value,
                                          Title = game.Title
                                      };
                }
                else
                {
                    activeGameModels = new List<ActiveGameModel>();
                }
                return activeGameModels.ToList();
            }
        }
        public static IEnumerable<MessageModel> GetAllMessages(int userId)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                var user = GetUser(userId, context);
                var messages = user.UserMessages;

                var messageModels =
                                   (from msg in messages
                                    select new MessageModel()
                                    {
                                        State = msg.MessageState.State,
                                        Text = msg.Text,
                                        GameId = (int)msg.Game.Id,
                                        GameTitle = msg.Game.Title,
                                        Type = msg.UserMessagesType.Type
                                    }).ToList();
                var readMessageState = context.MessageStates.First(st => st.State == "read");
                foreach (var msg in user.UserMessages)
                {
                    msg.MessageState = readMessageState;
                }
                context.SaveChanges();
                return messageModels;
            }
        }
        /* public methods */
        public static void CreateUser(string username, string nickname, string authCode)
        {
            ValidateUsername(username);
            ValidateNickname(nickname);
            ValidateAuthCode(authCode);
            using (BattleGameEntities context = new BattleGameEntities())
            {
                var usernameToLower = username.ToLower();
                var nicknameToLower = nickname.ToLower();

                var dbUser = context.Users.FirstOrDefault(u => u.Username == usernameToLower || u.Nickname.ToLower() == nicknameToLower);

                if (dbUser != null)
                {
                    if (dbUser.Username.ToLower() == usernameToLower)
                    {
                        throw new ServerErrorException("Username already exists", "ERR_DUP_USR");
                    }
                    else
                    {
                        throw new ServerErrorException("Nickname already exists", "ERR_DUP_NICK");
                    }
                }

                dbUser = new User()
                {
                    Username = usernameToLower,
                    Nickname = nickname,
                    AuthCode = authCode
                };
                context.Users.Add(dbUser);
                context.SaveChanges();
            }
        }
 private static Unit GetUnit(int unitId, BattleGameEntities context)
 {
     var unit = context.Units.FirstOrDefault(u => u.Id == unitId && u.HitPoints > 0);
     if (unit == null)
     {
         throw new ServerErrorException("No such unit", "ERR_INV_UNIT");
     }
     return unit;
 }
 protected static Game GetGame(int gameId, BattleGameEntities context)
 {
     var game = context.Games.FirstOrDefault(g => g.Id == gameId);
     if (game == null)
     {
         throw new ServerErrorException("No such game", "ERR_INV_GAME");
     }
     return game;
 }
 protected static User GetUser(int userId, BattleGameEntities context)
 {
     var user = context.Users.FirstOrDefault(u => u.Id == userId);
     if (user == null)
     {
         throw new ServerErrorException("Invalid user", "ERR_INV_USR");
     }
     return user;
 }
 protected static void SendMessage(string text, User toUser, Game game, UserMessagesType msgType, BattleGameEntities context)
 {
     toUser.UserMessages.Add(new UserMessage()
     {
         Game = game,
         MessageState = context.MessageStates.First(ms => ms.State == MessageStateUnread),
         UserMessagesType = msgType,
         Text = text
     });
 }
 protected static void ValidateGameInProgressStatus(Game game, BattleGameEntities context)
 {
     if (game.Status.Value == GameStatusOpen)
     {
         throw new ServerErrorException("Game not yet full", "INV_GAME_STATE");
     }
     else if (game.Status.Value == GameStatusFinished)
     {
         throw new ServerErrorException("Game is already finished", "INV_GAME_STATE");
     }
     else if (game.Status.Value == GameStatusFull)
     {
         throw new ServerErrorException("Game is not started yet", "INV_GAME_STATE");
     }
 }
 public static IEnumerable<UserModel> GetAllUsers()
 {
     var context = new BattleGameEntities();
     using (context)
     {
         var users =
             (from user in context.Users
              select new UserModel()
              {
                  Id = (int)user.Id,
                  Nickname = user.Nickname,
                  Score = user.Scores.Any() ? user.Scores.Sum(sc => sc.Value) : 0
              });
         return users.ToList();
     }
 }
        public static string LoginUser(string username, string authCode, out string nickname)
        {
            ValidateUsername(username);
            ValidateAuthCode(authCode);
            var context = new BattleGameEntities();
            using (context)
            {
                var usernameToLower = username.ToLower();
                var user = context.Users.FirstOrDefault(u => u.Username == usernameToLower && u.AuthCode == authCode);
                if (user == null)
                {
                    throw new ServerErrorException("Invalid username or password", "ERR_INV_USR");
                }

                var sessionKey = GenerateSessionKey((int)user.Id);
                user.SessionKey = sessionKey;
                nickname = user.Nickname;
                context.SaveChanges();
                return sessionKey;
            }
        }
        public static void StartGame(int gameId)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                var game = GetGame(gameId, context);
                if (game.Status.Value != GameStatusFull)
                {
                    throw new ServerErrorException("Nobody has joined this game", "INV_GAME_STATE");
                }

                UnitType warriorType = context.UnitTypes.First(type => type.Value == WarriorType);
                UnitType rangerType = context.UnitTypes.First(type => type.Value == RangerType);
                var redUnits = GenerateUnits(RedWarriorsPositionX, RedRangerPositionX, warriorType, rangerType);

                var attackMode = context.Modes.First(m => m.Value == UnitAttackMode);

                foreach (var unit in redUnits)
                {
                    unit.Mode = attackMode;
                    unit.User = game.RedUser;
                    game.Units.Add(unit);
                }

                var blueUnits = GenerateUnits(BlueWarriorsPositionX, BlueRangerPositionX, warriorType, rangerType);
                foreach (var unit in blueUnits)
                {
                    unit.Mode = attackMode;
                    unit.User = game.BlueUser;
                    game.Units.Add(unit);
                }

                game.Status = context.Statuses.First(st => st.Value == GameStatusInProgress);

                game.Turn = 0;
                var userInTurn = (rand.Next() % 2 == 0) ? game.RedUser : game.BlueUser;
                game.UserInTurn = userInTurn.Id;

                var gameStartedMessageText = string.Format("{0} just started game {1}", game.RedUser.Nickname, game.Title);
                var gameStartedMessageType = context.UserMessagesTypes.First(mt => mt.Type == UserMessageTypeGameStarted);
                SendMessage(gameStartedMessageText, game.BlueUser, game, gameStartedMessageType, context);

                var gameMoveMessageText = string.Format("It is your turn in game {0}", game.Title);
                var gameMoveMessageType = context.UserMessagesTypes.First(mt => mt.Type == UserMessageTypeGameMove);
                SendMessage(gameMoveMessageText, userInTurn, game, gameMoveMessageType, context);

                context.SaveChanges();
            }
        }
 public static void LogoutUser(string sessionKey)
 {
     ValidateSessionKey(sessionKey);
     var context = new BattleGameEntities();
     using (context)
     {
         var user = context.Users.FirstOrDefault(u => u.SessionKey == sessionKey);
         if (user == null)
         {
             throw new ServerErrorException("Invalid user authentication", "INV_USR_AUTH");
         }
         user.SessionKey = null;
         context.SaveChanges();
     }
 }
 public static int LoginUser(string sessionKey)
 {
     ValidateSessionKey(sessionKey);
     var context = new BattleGameEntities();
     using (context)
     {
         var user = context.Users.FirstOrDefault(u => u.SessionKey == sessionKey);
         if (user == null)
         {
             throw new ServerErrorException("Invalid user authentication", "INV_USR_AUTH");
         }
         return (int)user.Id;
     }
 }
        /* public members */

        public static void PerformMove(int userId, int gameId, int unitId, long toX, long toY)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                Game game = GetGame(gameId, context);
                ValidateGameInProgressStatus(game, context);

                User user = GetUser(userId, context);
                Unit unit = GetUnit(unitId, context);

                ValidateUserUnitInGame(user, unit, game);

                ValidateUnitMovePosition(game, unit, toX, toY);

                unit.Mode = context.Modes.First(m => m.Value == UnitModeAttack);
                unit.PositionX = toX;
                unit.PositionY = toY;

                var otherUser = (game.RedUser == user) ? game.BlueUser : game.RedUser;

                game.Turn++;
                game.UserInTurn = otherUser.Id;

                var messageText = string.Format(GameMoveMessageText, user.Nickname, game.Title);
                var gameMoveUserMessagesType = context.UserMessagesTypes.First(mt => mt.Type == GameMoveMessageType);
                SendMessage(messageText, otherUser, game, gameMoveUserMessagesType, context);
                context.SaveChanges();
            }
        }
 public static IEnumerable<OpenGameModel> GetOpenGames(int userId)
 {
     var context = new BattleGameEntities();
     using (context)
     {
         var user = GetUser(userId, context);
         var openStatus = context.Statuses.FirstOrDefault(st => st.Value == GameStatusOpen);
         IEnumerable<OpenGameModel> openGames;
         if (openStatus.Games.Any())
         {
             openGames =
                        from game in openStatus.Games
                        where game.RedUser != user
                        select new OpenGameModel()
                        {
                            Id = (int)game.Id,
                            Title = game.Title,
                            Creator = game.RedUser.Nickname
                        };
         }
         else
         {
             openGames = new List<OpenGameModel>();
         }
         return openGames.ToList();
     }
 }
        public static BattleFieldModel GetBattleField(int gameId)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                Game game = GetGame(gameId, context);
                ValidateGameInProgressStatus(game, context);

                var redUnitModels = GetUserUnits(game.RedUser, game);
                var blueUnitModels = GetUserUnits(game.BlueUser, game);

                var gameField = new BattleFieldModel()
                {
                    GameId = (int)game.Id,
                    Title = game.Title,
                    Red = new UserInGameModel()
                    {
                        Nickname = game.RedUser.Nickname,
                        Units = redUnitModels
                    },
                    Blue = new UserInGameModel()
                    {
                        Nickname = game.BlueUser.Nickname,
                        Units = blueUnitModels
                    },
                    Turn = game.Turn,
                    PlayerInTurn = (game.UserInTurn == game.RedUser.Id) ? "red" : "blue"
                };
                return gameField;
            }
        }
        public static void PerformAttack(int userId, int gameId, int unitId, long toX, long toY)
        {
            var context = new BattleGameEntities();
            using (context)
            {
                var game = GetGame(gameId, context);
                ValidateGameInProgressStatus(game, context);

                User user = GetUser(userId, context);
                Unit unit = GetUnit(unitId, context);

                ValidateUserUnitInGame(user, unit, game);

                ValidateUnitAttackPosition(game, unit, user, toX, toY);

                Unit attackedUnit = GetAttackedUnit(user, game, toX, toY);
                var attackedUnitArmor = attackedUnit.Armor;
                if (attackedUnit.Mode.Value == UnitModeDefend)
                {
                    attackedUnitArmor *= 2;
                }

                unit.Mode = context.Modes.First(m => m.Value == UnitModeAttack);
                var dmgTaken = unit.Attack - attackedUnitArmor;
                if (dmgTaken > 0)
                {
                    attackedUnit.HitPoints -= dmgTaken;
                }
                context.SaveChanges();

                var otherUser = (game.RedUser == user) ? game.BlueUser : game.RedUser;

                game.Turn++;

                if (game.Units.Any(u => u.User == otherUser && u.HitPoints > 0))
                {
                    game.UserInTurn = otherUser.Id;
                    var messageText = string.Format(GameMoveMessageText, user.Nickname, game.Title);
                    var gameMoveUserMessagesType = context.UserMessagesTypes.First(mt => mt.Type == GameMoveMessageType);
                    SendMessage(messageText, otherUser, game, gameMoveUserMessagesType, context);
                }
                else
                {
                    var finishedGameStatus = context.Statuses.First(st => st.Value == GameStatusFinished);
                    game.Status = finishedGameStatus;

                    var finishedGameMessageType = context.UserMessagesTypes.First(umt => umt.Type == UserMessageTypeGameFinished);

                    var messageTextWinner = string.Format("You won in game {0} against {1} in {2} moves!", game.Title, otherUser.Nickname, game.Turn);
                    SendMessage(messageTextWinner, user, game, finishedGameMessageType, context);

                    var messageTextLoser = string.Format("You were beaten in game {0} by {1}", game.Title, user.Nickname);
                    SendMessage(messageTextLoser, otherUser, game, finishedGameMessageType, context);

                    var score = game.Units.Where(u => u.User == user && u.HitPoints > 0).Sum(u => u.HitPoints);
                    user.Scores.Add(new Score()
                    {
                        Value = score
                    });
                }

                context.SaveChanges();
            }
        }
        public static void JoinGame(int userId, int gameId, string password)
        {
            if (password != null)
            {
                ValidateGamePassword(password);
            }
            var context = new BattleGameEntities();
            using (context)
            {
                var user = GetUser(userId, context);

                var game = context.Games.FirstOrDefault(g => g.Id == gameId);
                if (game == null)
                {
                    throw new ServerErrorException("No such game", "ERR_INV_GAME");
                }

                ValidateOpenGameStatus(game, context);

                if (game.Password != null && game.Password != password)
                {
                    throw new ServerErrorException("Invalid game password", "INV_GAME_PASS");
                }

                game.BlueUser = user;

                string messageText = string.Format("{0} just joined your game {1}", game.BlueUser.Nickname, game.Title);
                UserMessagesType gameJoinedUserMessageType = context.UserMessagesTypes.First(mt => mt.Type == UserMessageTypeGameJoined);
                SendMessage(messageText, game.RedUser, game, gameJoinedUserMessageType, context);

                var fullGameStatus = context.Statuses.First(st => st.Value == GameStatusFull);
                game.Status = fullGameStatus;

                context.SaveChanges();
            }
        }
        public static void PerformDefend(int userId, int gameId, int unitId)
        {
            var context = new BattleGameEntities();

            using (context)
            {
                var game = GetGame(gameId, context);
                ValidateGameInProgressStatus(game, context);

                var user = GetUser(userId, context);
                var unit = GetUnit(unitId, context);
                ValidateUserUnitInGame(user, unit, game);

                unit.Mode = context.Modes.First(m => m.Value == UnitModeDefend);
                
                var otherUser = (game.RedUser == user) ? game.BlueUser : game.RedUser;
                game.Turn++;
                game.UserInTurn = otherUser.Id;

                context.SaveChanges();
            }
        }