# Path: conf/ConfigWrapper.py
# class ConfigWrapper:
#     """
#     Used for loading and accessing values from a json config file.
#     """
#     def __init__(self, d: Dict):
#         self.d = d
# 
#     @staticmethod
#     def new(filename: str):
#         with open(filename) as confFile:
#             return ConfigWrapper(json.load(confFile))
# 
#     def __getitem__(self, key: str):
#         """
#         allows for accessing like, conf["index items like this"]
#         """
#         try:
#             head, tail = key.split(' ', 1)
#             return self.__getattr__(head)[tail]
#         except ValueError:
#             return self.__getattr__(key)
# 
#     def __getattr__(self, key: str):
#         """
#         allows for accessing like, conf.index.like.this
#         """
#         r = self.d.get(key)
#         if isinstance(r, dict):
#             return ConfigWrapper(r)
#         return r
# 
#     def asdict(self) -> Dict:
#         return self.d
# 
#     def __repr__(self):
#         return "ConfigWrapper({})".format(self.d)
# 
# Path: modules/game/EngineTools.py
# class EngineTools(NamedTuple('EngineTools', [
#         ('engine', Engine),
#         ('infoHandler', InfoHandler)
#     ])):
#     @staticmethod
#     def new(conf: ConfigWrapper):
#         engine = uci.popen_engine(stockfish_command(conf['stockfish update']))
#         engine.setoption({'Threads': conf['stockfish threads'], 'Hash': conf['stockfish memory']})
#         engine.uci()
# 
#         infoHandler = uci.InfoHandler()
# 
#         engine.info_handlers.append(infoHandler)
# 
#         return EngineTools(
#             engine=engine,
#             infoHandler=infoHandler)
# 
#     def analyseGame(self, game: Game, colour: Colour, nodes: int) -> Opt[AnalysedGame]:
#         gameLen = len(game.pgn)
#         if gameLen < 40 or gameLen > 120:
#             logging.warning(f'game too long/short to analyse ({gameLen} plys)')
#             return None
#         elif game.emts is None:
#             logging.warning(f'game has no emts')
#             return None
#         analysedMoves = []
# 
#         try:
#             playableGame = read_game(StringIO(" ".join(game.pgn)))
#         except ValueError:
#             return None
# 
#         node = playableGame
#         mainline_moves = [x for x in node.main_line()]
#         if len(game.emts) < len(mainline_moves):
#             logging.warning(f"Not enough emts. len(emts): {len(game.emts)} vs len(node.main_line()): {len(mainline_moves)}")
#             return None
# 
#         self.engine.ucinewgame()
# 
#         while not node.is_end():
#             logging.info(f'analysing position\n{node.board()}\n')
#             nextNode = node.variation(0)
#             if colour == node.board().turn: ## if it is the turn of the player of interest
#                 self.engine.setoption({'multipv': 5})
#                 self.engine.position(node.board())
#                 self.engine.go(nodes=nodes)
# 
#                 analyses = list([
#                     Analysis(
#                         pv[1][0].uci(),
#                         EngineEval(engineEval[1].cp, engineEval[1].mate)) for engineEval, pv in zip(
#                             self.infoHandler.info['score'].items(),
#                             self.infoHandler.info['pv'].items())])
# 
#                 self.engine.setoption({'multipv': 1})
#                 self.engine.position(nextNode.board())
#                 self.engine.go(nodes=nodes)
# 
#                 engineEval = EngineEval(
#                     self.infoHandler.info['score'][1].cp,
#                     self.infoHandler.info['score'][1].mate).inverse() # flipped because analysing from other player side
# 
#                 moveNumber = node.board().fullmove_number
# 
#                 analysedMoves.append(AnalysedMove(
#                     uci = node.variation(0).move.uci(),
#                     move = moveNumber,
#                     emt = game.emts[EngineTools.ply(moveNumber, colour)],
#                     engineEval = engineEval,
#                     analyses = analyses))
# 
#             node = nextNode
# 
#         playerId = game.white if colour else game.black
#         return AnalysedGame.new(game.id, colour, playerId, analysedMoves)
# 
#     @staticmethod
#     def ply(moveNumber, colour: Colour) -> int:
#         return (2*(moveNumber-1)) + (0 if colour else 1)


from default_imports import *
from conf.ConfigWrapper import ConfigWrapper
from modules.game.EngineTools import EngineTools



class Env:
    def __init__(self, config: ConfigWrapper, token: Opt[str] = None):
        self.config = config
        self.url = "{}://{}".format(self.config.server.protocol, self.config.server.domain)
        self.engineTools = EngineTools.new(self.config)
        if token is None:
            self.auth = self.config.auth.asdict()

# Path: conf/ConfigWrapper.py
# class ConfigWrapper:
#     """
#     Used for loading and accessing values from a json config file.
#     """
#     def __init__(self, d: Dict):
#         self.d = d
# 
#     @staticmethod
#     def new(filename: str):
#         with open(filename) as confFile:
#             return ConfigWrapper(json.load(confFile))
# 
#     def __getitem__(self, key: str):
#         """
#         allows for accessing like, conf["index items like this"]
#         """
#         try:
#             head, tail = key.split(' ', 1)
#             return self.__getattr__(head)[tail]
#         except ValueError:
#             return self.__getattr__(key)
# 
#     def __getattr__(self, key: str):
#         """
#         allows for accessing like, conf.index.like.this
#         """
#         r = self.d.get(key)
#         if isinstance(r, dict):
#             return ConfigWrapper(r)
#         return r
# 
#     def asdict(self) -> Dict:
#         return self.d
# 
#     def __repr__(self):
#         return "ConfigWrapper({})".format(self.d)
# 
# Path: modules/game/EngineTools.py
# class EngineTools(NamedTuple('EngineTools', [
#         ('engine', Engine),
#         ('infoHandler', InfoHandler)
#     ])):
#     @staticmethod
#     def new(conf: ConfigWrapper):
#         engine = uci.popen_engine(stockfish_command(conf['stockfish update']))
#         engine.setoption({'Threads': conf['stockfish threads'], 'Hash': conf['stockfish memory']})
#         engine.uci()
# 
#         infoHandler = uci.InfoHandler()
# 
#         engine.info_handlers.append(infoHandler)
# 
#         return EngineTools(
#             engine=engine,
#             infoHandler=infoHandler)
# 
#     def analyseGame(self, game: Game, colour: Colour, nodes: int) -> Opt[AnalysedGame]:
#         gameLen = len(game.pgn)
#         if gameLen < 40 or gameLen > 120:
#             logging.warning(f'game too long/short to analyse ({gameLen} plys)')
#             return None
#         elif game.emts is None:
#             logging.warning(f'game has no emts')
#             return None
#         analysedMoves = []
# 
#         try:
#             playableGame = read_game(StringIO(" ".join(game.pgn)))
#         except ValueError:
#             return None
# 
#         node = playableGame
#         mainline_moves = [x for x in node.main_line()]
#         if len(game.emts) < len(mainline_moves):
#             logging.warning(f"Not enough emts. len(emts): {len(game.emts)} vs len(node.main_line()): {len(mainline_moves)}")
#             return None
# 
#         self.engine.ucinewgame()
# 
#         while not node.is_end():
#             logging.info(f'analysing position\n{node.board()}\n')
#             nextNode = node.variation(0)
#             if colour == node.board().turn: ## if it is the turn of the player of interest
#                 self.engine.setoption({'multipv': 5})
#                 self.engine.position(node.board())
#                 self.engine.go(nodes=nodes)
# 
#                 analyses = list([
#                     Analysis(
#                         pv[1][0].uci(),
#                         EngineEval(engineEval[1].cp, engineEval[1].mate)) for engineEval, pv in zip(
#                             self.infoHandler.info['score'].items(),
#                             self.infoHandler.info['pv'].items())])
# 
#                 self.engine.setoption({'multipv': 1})
#                 self.engine.position(nextNode.board())
#                 self.engine.go(nodes=nodes)
# 
#                 engineEval = EngineEval(
#                     self.infoHandler.info['score'][1].cp,
#                     self.infoHandler.info['score'][1].mate).inverse() # flipped because analysing from other player side
# 
#                 moveNumber = node.board().fullmove_number
# 
#                 analysedMoves.append(AnalysedMove(
#                     uci = node.variation(0).move.uci(),
#                     move = moveNumber,
#                     emt = game.emts[EngineTools.ply(moveNumber, colour)],
#                     engineEval = engineEval,
#                     analyses = analyses))
# 
#             node = nextNode
# 
#         playerId = game.white if colour else game.black
#         return AnalysedGame.new(game.id, colour, playerId, analysedMoves)
# 
#     @staticmethod
#     def ply(moveNumber, colour: Colour) -> int:
#         return (2*(moveNumber-1)) + (0 if colour else 1)


# Path: modules/client/Env.py
from default_imports import *
from conf.ConfigWrapper import ConfigWrapper
from modules.game.EngineTools import EngineTools




class Env:
    def __init__(self, config: ConfigWrapper, token: Opt[str] = None):
        self.config = config
        self.url = "{}://{}".format(self.config.server.protocol, self.config.server.domain)
        self.engineTools = EngineTools.new(self.config)
        if token is None:
            self.auth = self.config.auth.asdict()

        else: