private static async Task CrawlGames(ILogger logger, GamesListingOptions gameListingOptions)
        {
            using (FicsClient client = new FicsClient())
            {
                await client.Login(configuration.FicsUsername, configuration.FicsPassword);
                client.ServerVariables.ShowPromptTime = false;
                client.ServerVariables.Seek = false;
                client.ServerVariables.MoveBell = false;
                client.ServerVariables.Style = 12;
                client.ServerVariables.ShowProvisionalRatings = true;
                client.ServerVariables.Interface = "GameCrawler";
                client.ServerInterfaceVariables.PreciseTimes = true;
                client.ServerInterfaceVariables.DetailedGameInfo = true;
                client.ServerInterfaceVariables.PreMove = true;
                client.ServerInterfaceVariables.SmartMove = false;

                client.UnknownMessageReceived += (message) =>
                {
                    logger.LogUnknownMessage(message);
                };

                client.GameStateChange += (state) =>
                {
                    ObservingGame game;

                    lock (observingGames)
                    {
                        if (!observingGames.TryGetValue(state.GameId, out game))
                            return;
                    }

                    if (state.LastMove != null && state.LastMove != "none")
                    {
                        var move = new ChessMove()
                        {
                            Move = state.LastMove,
                            Time = state.LastMoveTime,
                        };

                        List<ChessMove> movesList = state.WhiteMove ? game.BlackMovesList : game.WhiteMovesList;
                        int moveNumber = !state.WhiteMove ? state.Move : state.Move - 1;

                        lock (movesList)
                        {
                            while (movesList.Count < moveNumber)
                            {
                                movesList.Add(null);
                            }

                            movesList[moveNumber - 1] = move;
                        }
                    }
                };

                client.GameEnded += (result) =>
                {
                    ObservingGame game;

                    lock (observingGames)
                    {
                        if (observingGames.TryGetValue(result.GameId, out game))
                        {
                            game.Result = result;
                            Log("Game ended {0}\nWhite moves: {1}\nBlack moves: {2}", game.Game, game.WhiteMovesList.Count, game.BlackMovesList.Count);
                            observingGames.Remove(result.GameId);
                        }
                    }

                    // Check if game was aborted and save otherwise
                    if (game != null && result.WhitePlayerPoints + result.BlackPlayerPoints > 0)
                    {
                        bool save = true;

                        if (game.PartnersGame != null)
                        {
                            if (game.Game.WhitePlayer.Username.CompareTo(game.PartnersGame.Game.WhitePlayer.Username) < 0)
                            {
                                lock (game)
                                lock (game.PartnersGame)
                                    {
                                        game.Finished = true;
                                        if (!game.PartnersGame.Finished)
                                            save = false;
                                    }
                            }
                            else
                            {
                                lock (game.PartnersGame)
                                lock (game)
                                    {
                                        game.Finished = true;
                                        if (!game.PartnersGame.Finished)
                                            save = false;
                                    }
                            }
                        }

                        if (save)
                            logger.SaveGame(game);
                    }
                };

                while (client.ConnectionWorking)
                {
                    var games = await client.ListGames(gameListingOptions);

                    Log("Active games: {0}", games.Count);
                    foreach (var game in games)
                    {
                        if (game.Examined || game.InSetup)
                            continue;

                        bool containsKey;

                        lock (observingGames)
                        {
                            containsKey = observingGames.ContainsKey(game.Id);
                        }

                        if (!containsKey)
                        {
                            try
                            {
                                // Add game to the list
                                var observingGame = new ObservingGame();
                                observingGame.BlackMovesList = new List<ChessMove>();
                                observingGame.WhiteMovesList = new List<ChessMove>();
                                observingGame.Game = game;
                                lock (observingGames)
                                {
                                    observingGames.Add(game.Id, observingGame);
                                }

                                // Start observing game
                                var result = await client.StartObservingGame(game);

                                // If we started observing different game, stop observing it
                                if (!result.GameInfo.WhitePlayer.Username.StartsWith(game.WhitePlayer.Username)
                                    || !result.GameInfo.BlackPlayer.Username.StartsWith(game.BlackPlayer.Username))
                                {
                                    Log("Canceling game {0}", game);
                                    await client.StopObservingGame(game);
                                }


                                Log("Starting game {0}", game);

                                // Collect and update moves list
                                var moveList = await client.GetMoveList(game);

                                observingGame.GameStarted = moveList.GameStarted;
                                lock (observingGame.WhiteMovesList)
                                {
                                    while (observingGame.WhiteMovesList.Count < moveList.WhiteMoves.Count)
                                        observingGame.WhiteMovesList.Add(null);
                                    for (int i = 0; i < moveList.WhiteMoves.Count; i++)
                                        observingGame.WhiteMovesList[i] = moveList.WhiteMoves[i];
                                }

                                lock (observingGame.BlackMovesList)
                                {
                                    while (observingGame.BlackMovesList.Count < moveList.BlackMoves.Count)
                                        observingGame.BlackMovesList.Add(null);
                                    for (int i = 0; i < moveList.BlackMoves.Count; i++)
                                        observingGame.BlackMovesList[i] = moveList.BlackMoves[i];
                                }

                                // Connect partners game
                                if (result.GameInfo.PartnersGameId > 0)
                                {
                                    ObservingGame partnersGame;

                                    lock (observingGames)
                                    {
                                        if (observingGames.TryGetValue(result.GameInfo.PartnersGameId, out partnersGame))
                                        {
                                            partnersGame.PartnersGame = observingGame;
                                            observingGame.PartnersGame = partnersGame;
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                logger.LogException(ex);
                                lock (observingGames)
                                {
                                    observingGames.Remove(game.Id);
                                }

                                try
                                {
                                    await client.StopObservingGame(game.Id);
                                }
                                catch (Exception)
                                {
                                    // We want to swallow this exception
                                }
                            }
                        }
                    }

                    await Task.Delay(TimeSpan.FromSeconds(10));
                }
            }
        }