from DistributedMinigameAI import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
import VineGameGlobals

class DistributedVineGameAI(DistributedMinigameAI):

    def __init__(self, air, minigameId):
        try:
            self.DistributedVineGameAI_initialized
        except:
            self.DistributedVineGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedVineGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']),
             State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'waitShowScores']),
             State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup']),
             State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
            self.toonInfo = {}
            self.addChildGameFSM(self.gameFSM)
            self.vineSections = []
            self.finishedBonus = {}
            self.finishedTimeLeft = {}
            self.totalSpiders = 0
            self.calculatedPartialBeans = False

    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def _playing(self):
        if not hasattr(self, 'gameFSM'):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() == 'play'

    def setGameReady(self):
        self.notify.debug('setGameReady')
        for avId in self.avIdList:
            self.updateToonInfo(avId, vineIndex=0, vineT=VineGameGlobals.VineStartingT)

        DistributedMinigameAI.setGameReady(self)
        self.numTreasures = VineGameGlobals.NumVines - 1
        self.numTreasuresTaken = 0
        self.takenTable = [0] * self.numTreasures
        for avId in self.scoreDict.keys():
            self.scoreDict[avId] = 0
            self.finishedBonus[avId] = 0
            self.finishedTimeLeft[avId] = -1

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug('gameOver')
        vineReached = []
        scoreList = []
        finishedList = []
        timeLeftList = []
        for avId in self.avIdList:
            vineReached.append(self.toonInfo[avId][0])
            scoreList.append(self.scoreDict[avId])
            finishedList.append(self.finishedBonus[avId])
            timeLeftList.append(self.finishedTimeLeft[avId])

        totalBats = len(VineGameGlobals.BatInfo[self.getSafezoneId()])
        self.air.writeServerEvent('minigame_vine', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.VineGameId,
         self.getSafezoneId(),
         self.avIdList,
         scoreList,
         self.vineSections,
         finishedList,
         timeLeftList,
         vineReached,
         self.totalSpiders,
         totalBats))
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

    def enterInactive(self):
        self.notify.debug('enterInactive')

    def exitInactive(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.vines = []
        index = 0
        taskMgr.doMethodLater(VineGameGlobals.GameDuration, self.timerExpired, self.taskName('gameTimer'))

    def exitPlay(self):
        taskMgr.remove(self.taskName('gameTimer'))
        for vine in self.vines:
            vine.requestDelete()

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass

    def claimTreasure(self, treasureNum):
        if not self._playing():
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.scoreDict:
            self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId,
             treasureNum,
             self.scoreDict,
             self.avIdList))
            return
        if treasureNum < 0 or treasureNum >= self.numTreasures:
            self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range')
            return
        if self.takenTable[treasureNum]:
            return
        self.takenTable[treasureNum] = 1
        avId = self.air.getAvatarIdFromSender()
        self.sendUpdate('setTreasureGrabbed', [avId, treasureNum])
        self.scoreDict[avId] += 1
        self.numTreasuresTaken += 1

    def timerExpired(self, task):
        self.notify.debug('timer expired')
        if not VineGameGlobals.EndlessGame:
            self.gameFSM.request('waitShowScores')
        return Task.done

    def enterWaitShowScores(self):
        self.notify.debug('enterWaitShowScores')
        self.awardPartialBeans()
        taskMgr.doMethodLater(VineGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores'))

    def __doneShowingScores(self, task):
        self.notify.debug('doneShowingScores')
        self.gameOver()
        return Task.done

    def exitWaitShowScores(self):
        taskMgr.remove(self.taskName('waitShowScores'))

    def reachedEndVine(self, vineIndex):
        self.notify.debug('reachedEndVine')
        return
        avId = self.air.getAvatarIdFromSender()
        oldVineIndex = self.toonInfo[avId][0]
        self.updateToonInfo(avId, vineIndex=vineIndex)
        if not oldVineIndex == vineIndex:
            self.checkForEndVine(avId)
            self.checkForEndGame()

    def setNewVine(self, avId, vineIndex, vineT, facingRight):
        self.notify.debug('setNewVine')
        if not self._playing():
            return
        if avId not in self.avIdList:
            self.air.writeServerEvent('suspicious', avId, 'VineGameAI.setNewVine: invalid avId')
            return
        oldVineIndex = self.toonInfo[avId][0]
        debugStr = 'setNewVine doId=%s avId=%d vineIndex=%s oldVineIndex=%s' % (self.doId,
         avId,
         vineIndex,
         oldVineIndex)
        self.updateToonInfo(avId, vineIndex=vineIndex, vineT=vineT, facingRight=facingRight)
        if not oldVineIndex == vineIndex:
            self.checkForEndVine(avId)
            self.checkForEndGame()

    def checkForEndGame(self):
        allDone = True
        for avId in self.toonInfo:
            if not self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1:
                allDone = False
                break

        if allDone:
            if not VineGameGlobals.EndlessGame:
                self.awardPartialBeans()
                self.sendUpdate('allAtEndVine', [])
                self.gameOver()

    def checkForEndVine(self, avId):
        if self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1:
            curTime = self.getCurrentGameTime()
            timeLeft = VineGameGlobals.GameDuration - curTime
            self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft))
            if avId not in self.scoreDict:
                self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId,
                 treasureNum,
                 self.scoreDict,
                 self.avIdList))
                return
            addBonus = int(VineGameGlobals.BaseBonusOnEndVine[self.getSafezoneId()] + VineGameGlobals.BonusPerSecondLeft * timeLeft)
            self.notify.debug('addBOnus = %d' % addBonus)
            if addBonus < 0:
                addBonus = 0
            self.finishedBonus[avId] = addBonus
            timeLeftStr = '%.1f' % timeLeft
            self.finishedTimeLeft[avId] = timeLeftStr
            self.scoreDict[avId] += addBonus
            self.sendUpdate('setScore', [avId, self.scoreDict[avId]])

    def updateToonInfo(self, avId, vineIndex = None, vineT = None, posX = None, posZ = None, facingRight = None, climbDir = None, velX = None, velZ = None):
        newVineIndex = vineIndex
        newVineT = vineT
        newPosX = posX
        newPosZ = posZ
        newFacingRight = facingRight
        newClimbDir = climbDir
        newVelX = velX
        newVelZ = velZ
        oldInfo = None
        if avId in self.toonInfo:
            oldInfo = self.toonInfo[avId]
            if vineIndex == None:
                newVineIndex = oldInfo[0]
            if vineT == None:
                newVineT = oldInfo[1]
            if posX == None:
                newPosX = oldInfo[2]
            if posZ == None:
                newPosZ = oldInfo[3]
            if facingRight == None:
                newFacingRight = oldInfo[4]
            if climbDir == None:
                newClimbDir = oldInfo[5]
            if velX == None:
                newVelX = oldInfo[6]
            if velZ == None:
                newVelZ = oldInfo[7]
        if newVineIndex < -1 or newVineIndex >= VineGameGlobals.NumVines:
            newVineIndex = 0
        if newVineT < 0 or newVineT > 1:
            pass
        if not newFacingRight == 0 and not newFacingRight == 1:
            newFacingRight = 1
        if newPosX < -1000 or newPosX > 2000:
            newPosX = 0
        if newPosZ < -100 or newPosZ > 1000:
            newPosZ = 0
        if newVelX < -1000 or newVelX > 1000:
            newVelX = 0
        if newVelZ < -1000 or newVelZ > 1000:
            newVelZ = 0
        newInfo = [newVineIndex,
         newVineT,
         newPosX,
         newPosZ,
         newFacingRight,
         newClimbDir,
         newVelX,
         newVelZ]
        self.toonInfo[avId] = newInfo
        return

    def setupVineSections(self):
        szId = self.getSafezoneId()
        courseWeights = VineGameGlobals.CourseWeights[szId]
        pool = [[],
         [],
         [],
         [],
         [],
         []]
        for weights in courseWeights:
            section, chances = weights
            numSpiders = VineGameGlobals.getNumSpidersInSection(section)
            pool[numSpiders] += [section] * chances

        maxSpiders = VineGameGlobals.SpiderLimits[szId]
        curSpiders = 0
        for i in xrange(4):
            spidersLeft = maxSpiders - curSpiders
            validChoices = []
            for numSpiders in xrange(spidersLeft + 1):
                validChoices += pool[numSpiders]

            if not validChoices:
                self.notify.warning('we ran out of valid choices szId=%s, vineSections=%s' % (szId, self.vineSections))
                validChoices += [0]
            section = random.choice(validChoices)
            curSpiders += VineGameGlobals.getNumSpidersInSection(section)
            self.vineSections.append(section)

        self.totalSpiders = curSpiders
        self.notify.debug('calc vineSections = %s' % self.vineSections)

    def getVineSections(self):
        return self.vineSections

    def setTrolleyZone(self, trolleyZone):
        DistributedMinigameAI.setTrolleyZone(self, trolleyZone)
        self.setupVineSections()

    def awardPartialBeans(self):
        if self.calculatedPartialBeans:
            return
        self.calculatedPartialBeans = True
        for avId in self.avIdList:
            vineIndex = self.toonInfo[avId][0]
            if not vineIndex == VineGameGlobals.NumVines - 1:
                partialBeans = int(vineIndex / 5.0)
                if avId in self.scoreDict:
                    self.scoreDict[avId] += partialBeans
                    self.sendUpdate('setScore', [avId, self.scoreDict[avId]])
