# Trosnoth (UberTweak Platform Game)
# Copyright (C) 2006-2012 Joshua D Bartlett
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

MAX_SHOTS = 1 << 32 - 1
MAX_STARS = 255


class IdManager(object):
    '''
    Designed to be plugged on the server side. Takes all messages passed in
    (usually generated by the validator and universe) and passes them on after
    assigning shot ids, player ids, etc.
    '''

    def __init__(self, world):
        super(IdManager, self).__init__()
        self.world = world
        self._usedPlayers = set()
        self._nextPlayer = 1
        self._usedShots = set()
        self._nextShot = 1
        self._usedStars = set()
        self._nextStar = 1
        world.onShotRemoved.addListener(self.shotRemoved)
        world.onCollectableStarRemoved.addListener(self.collectableStarRemoved)
        world.onPlayerRemoved.addListener(self.playerRemoved)

    def stop(self):
        self.world.onShotRemoved.removeListener(self.shotRemoved)
        self.world.onCollectableStarRemoved.removeListener(
            self.collectableStarRemoved)
        self.world.onPlayerRemoved.removeListener(self.playerRemoved)

    def newShotId(self):
        shotId = self._nextShot
        if len(self._usedShots) >= MAX_SHOTS:
            return  None    # Cannot create any more shots.
        while shotId in self._usedShots:
            shotId = shotId % MAX_SHOTS + 1
        self._usedShots.add(shotId)
        self._nextShot = shotId % MAX_SHOTS + 1
        return shotId

    def shotRemoved(self, shotId):
        self._usedShots.discard(shotId)

    def newStarId(self):
        starId = self._nextStar
        if len(self._usedStars) >= MAX_STARS:
            return None     # Cannot create any more stars.
        while starId in self._usedStars:
            starId = starId % MAX_STARS + 1
        self._usedStars.add(starId)
        self._nextStar = starId % MAX_STARS + 1
        return chr(starId)

    def collectableStarRemoved(self, starId):
        self._usedStars.remove(ord(starId))

    def newPlayerId(self):
        playerId = self._nextPlayer
        if len(self._usedPlayers) >= 255:
            # Cannot join: hard max on player ids.
            return None

        while playerId in self._usedPlayers:
            playerId = playerId % 255 + 1
        self._usedPlayers.add(playerId)
        self._nextPlayer = playerId % 255 + 1

        return chr(playerId)

    def playerRemoved(self, player, playerId):
        self._usedPlayers.discard(ord(playerId))
