# Path: modules/game/AnalysedGame.py
# class AnalysedGameBSONHandler:
#     @staticmethod
#     def reads(bson: Dict) -> AnalysedGame:
#         return AnalysedGame(
#             id = bson['_id'],
#             playerId = bson['userId'],
#             gameId = bson['gameId'],
#             analysedMoves = [AnalysedMoveBSONHandler.reads(am) for am in bson['analysis']])
# 
#     @staticmethod
#     def writes(analysedGame: AnalysedGame) -> Dict:
#         return {
#             '_id': analysedGame.id,
#             'userId': analysedGame.playerId,
#             'gameId': analysedGame.gameId,
#             'analysis': [AnalysedMoveBSONHandler.writes(am) for am in analysedGame.analysedMoves]
#         }
# 
# Path: modules/game/Env.py
# class Env:
#     def __init__(self, config: ConfigWrapper, db: Database):
#         self.config = config
#         self.db = db
# 
#         self.gameDB = GameDB(self.db[self.config["game coll game"]])
#         self.analysedGameDB = AnalysedGameDB(self.db[self.config["game coll analysed_game"]])
#         self.playerDB = PlayerDB(self.db[self.config["game coll player"]])
#         self.analysedPositionDB = AnalysedPositionDB(self.db[self.config["game coll analysed_position"]])
# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
#     @staticmethod
#     def fromJson(userData: Dict):
#         try:
#             return Player(
#                 id=userData['id'],
#                 titled=userData['titled'],
#                 engine=userData['engine'],
#                 gamesPlayed=userData['games'])
#         except (RuntimeTypeError, AttributeError):
#             logging.debug("something's fucked")
#         return None
# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
# class PlayerBSONHandler:
# class PlayerDB(NamedTuple('PlayerDB', [
#         ('playerColl', 'Collection')
#     ])):
#     def fromJson(userData: Dict):
#     def reads(bson: Dict) -> Player:
#     def writes(player: Player) -> Dict:
#     def byId(self, playerId: PlayerID) -> Opt[Player]:
#     def byPlayerId(self, playerId: PlayerID) -> Opt[Player]:
#     def unmarkedByUserIds(self, playerIds: List[PlayerID]) -> List[Player]:
#     def engineSample(self, engine: bool, size: int) -> List[Player]:
#     def oldestNonEngine(self) -> Opt[Player]:
#     def byEngine(self, engine: bool = True) -> List[Player]:
#     def all(self) -> List[Player]:
#     def write(self, player: Player):
# 
# Path: modules/game/Game.py
# class Game(NamedTuple('Game', [
#         ('id', GameID),
#         ('white', PlayerID),
#         ('black', PlayerID),
#         ('pgn', List[str]),
#         ('emts', Opt[List[Emt]]),
#         ('analysis', Analysis)
#     ])):
# class GameBSONHandler:
# class GameDB(NamedTuple('GameDB', [
#         ('gameColl', Collection)
#     ])):
#     def fromDict(d: Dict):
#     def fromJson(json: Dict):
#     def toJson(self):
#     def playable(self):
#     def boardTensors(self, colour):
#     def boardTensorsByPlayerId(self, playerId: PlayerID, length: int = 60, safe: bool = True):
#     def tensor(self, playerId: PlayerID, length: int = 60, noisey: bool = False, safe: bool = True) -> Opt[GameTensor]:
#     def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
#     def moveTensor(analysis: Analysis, emt: Emt, boardTensor: List[int], avgEmt: Number, colour: Colour) -> MoveTensor:
#     def nullBoardTensor():
#     def nullMoveTensor() -> MoveTensor:
#     def ply(moveNumber: int, colour: Colour) -> int:
#     def reads(bson: Dict) -> Game:
#     def writes(game: Game) -> Dict:
#     def byId(self, _id: GameID) -> Opt[Game]:
#     def byIds(self, ids: List[GameID]) -> List[Game]:
#     def byPlayerId(self, playerId: PlayerID) -> List[Game]:
#     def byPlayerIdAndAnalysed(self, playerId: PlayerID, analysed: bool = True) -> List[Game]:
#     def write(self, game: Game):
#     def writeMany(self, games: List[Game]):


from default_imports import *
from modules.game.AnalysedGame import AnalysedGameBSONHandler
from modules.game.Env import Env
from modules.game.Player import Player
from modules.game.Player import PlayerID
from modules.game.Game import Game, GameID
import logging
    def writeAnalysedGames(self, analysedGamesBSON: List[Dict]) -> bool:
        try:
            analysedGames = [AnalysedGameBSONHandler.reads(g) for g in analysedGamesBSON]
            self.env.analysedGameDB.writeMany(analysedGames)
            return True
        except (KeyError, ValueError):
            logging.warning('Malformed analysedGamesBSON: ' + str(analysedGamesBSON))
        return False

    def gamesForAnalysis(self, playerId: PlayerID, required: List[str] = []) -> List[Game]:
        """
        Given a playerId and an amount of games. This function will return the games within `limit`
        that should be analysed
        """
        games = self.env.gameDB.byPlayerId(playerId)
        analysedGames = self.env.analysedGameDB.byPlayerId(playerId)

        gameIds = {g.id for g in games}
        analysedGameIds = {g.gameId for g in analysedGames}

        notAnalysedButRequiredIds = set(required) - analysedGameIds

        correct_length = lambda g: len(g.pgn) >= 40 and len(g.pgn) <= 120
        games = [g for g in games if g.id in notAnalysedButRequiredIds and correct_length(g)]

        return games

    def gamesByIds(self, gameIds: List[GameID]):
        return self.env.gameDB.byIds(gameIds)


# Path: modules/game/AnalysedGame.py
# class AnalysedGameBSONHandler:
#     @staticmethod
#     def reads(bson: Dict) -> AnalysedGame:
#         return AnalysedGame(
#             id = bson['_id'],
#             playerId = bson['userId'],
#             gameId = bson['gameId'],
#             analysedMoves = [AnalysedMoveBSONHandler.reads(am) for am in bson['analysis']])
# 
#     @staticmethod
#     def writes(analysedGame: AnalysedGame) -> Dict:
#         return {
#             '_id': analysedGame.id,
#             'userId': analysedGame.playerId,
#             'gameId': analysedGame.gameId,
#             'analysis': [AnalysedMoveBSONHandler.writes(am) for am in analysedGame.analysedMoves]
#         }
# 
# Path: modules/game/Env.py
# class Env:
#     def __init__(self, config: ConfigWrapper, db: Database):
#         self.config = config
#         self.db = db
# 
#         self.gameDB = GameDB(self.db[self.config["game coll game"]])
#         self.analysedGameDB = AnalysedGameDB(self.db[self.config["game coll analysed_game"]])
#         self.playerDB = PlayerDB(self.db[self.config["game coll player"]])
#         self.analysedPositionDB = AnalysedPositionDB(self.db[self.config["game coll analysed_position"]])
# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
#     @staticmethod
#     def fromJson(userData: Dict):
#         try:
#             return Player(
#                 id=userData['id'],
#                 titled=userData['titled'],
#                 engine=userData['engine'],
#                 gamesPlayed=userData['games'])
#         except (RuntimeTypeError, AttributeError):
#             logging.debug("something's fucked")
#         return None
# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
# class PlayerBSONHandler:
# class PlayerDB(NamedTuple('PlayerDB', [
#         ('playerColl', 'Collection')
#     ])):
#     def fromJson(userData: Dict):
#     def reads(bson: Dict) -> Player:
#     def writes(player: Player) -> Dict:
#     def byId(self, playerId: PlayerID) -> Opt[Player]:
#     def byPlayerId(self, playerId: PlayerID) -> Opt[Player]:
#     def unmarkedByUserIds(self, playerIds: List[PlayerID]) -> List[Player]:
#     def engineSample(self, engine: bool, size: int) -> List[Player]:
#     def oldestNonEngine(self) -> Opt[Player]:
#     def byEngine(self, engine: bool = True) -> List[Player]:
#     def all(self) -> List[Player]:
#     def write(self, player: Player):
# 
# Path: modules/game/Game.py
# class Game(NamedTuple('Game', [
#         ('id', GameID),
#         ('white', PlayerID),
#         ('black', PlayerID),
#         ('pgn', List[str]),
#         ('emts', Opt[List[Emt]]),
#         ('analysis', Analysis)
#     ])):
# class GameBSONHandler:
# class GameDB(NamedTuple('GameDB', [
#         ('gameColl', Collection)
#     ])):
#     def fromDict(d: Dict):
#     def fromJson(json: Dict):
#     def toJson(self):
#     def playable(self):
#     def boardTensors(self, colour):
#     def boardTensorsByPlayerId(self, playerId: PlayerID, length: int = 60, safe: bool = True):
#     def tensor(self, playerId: PlayerID, length: int = 60, noisey: bool = False, safe: bool = True) -> Opt[GameTensor]:
#     def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
#     def moveTensor(analysis: Analysis, emt: Emt, boardTensor: List[int], avgEmt: Number, colour: Colour) -> MoveTensor:
#     def nullBoardTensor():
#     def nullMoveTensor() -> MoveTensor:
#     def ply(moveNumber: int, colour: Colour) -> int:
#     def reads(bson: Dict) -> Game:
#     def writes(game: Game) -> Dict:
#     def byId(self, _id: GameID) -> Opt[Game]:
#     def byIds(self, ids: List[GameID]) -> List[Game]:
#     def byPlayerId(self, playerId: PlayerID) -> List[Game]:
#     def byPlayerIdAndAnalysed(self, playerId: PlayerID, analysed: bool = True) -> List[Game]:
#     def write(self, game: Game):
#     def writeMany(self, games: List[Game]):


# Path: modules/game/Api.py
from default_imports import *
from modules.game.AnalysedGame import AnalysedGameBSONHandler
from modules.game.Env import Env
from modules.game.Player import Player
from modules.game.Player import PlayerID
from modules.game.Game import Game, GameID
import logging

    def writeAnalysedGames(self, analysedGamesBSON: List[Dict]) -> bool:
        try:
            analysedGames = [AnalysedGameBSONHandler.reads(g) for g in analysedGamesBSON]
            self.env.analysedGameDB.writeMany(analysedGames)
            return True
        except (KeyError, ValueError):
            logging.warning('Malformed analysedGamesBSON: ' + str(analysedGamesBSON))
        return False

    def gamesForAnalysis(self, playerId: PlayerID, required: List[str] = []) -> List[Game]:
        """
        Given a playerId and an amount of games. This function will return the games within `limit`
        that should be analysed
        """
        games = self.env.gameDB.byPlayerId(playerId)
        analysedGames = self.env.analysedGameDB.byPlayerId(playerId)

        gameIds = {g.id for g in games}
        analysedGameIds = {g.gameId for g in analysedGames}

        notAnalysedButRequiredIds = set(required) - analysedGameIds

        correct_length = lambda g: len(g.pgn) >= 40 and len(g.pgn) <= 120
        games = [g for g in games if g.id in notAnalysedButRequiredIds and correct_length(g)]

        return games

    def gamesByIds(self, gameIds: List[GameID]):
        return self.env.gameDB.byIds(gameIds)


    def writeGames(self, games: List[Game]):