from pandac.PandaModules import *
from toontown.toonbase.ToonBaseGlobal import *
from toontown.toonbase.ToontownGlobals import GlobalDialogColor
from DistributedMinigame import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from toontown.toonbase import TTLocalizer
from toontown.toonbase import ToontownTimer
import TravelGameGlobals
import math
from pandac.PandaModules import rad2Deg
from toontown.toontowngui import TTDialog
from direct.interval.IntervalGlobal import *
import VoteResultsPanel
import VoteResultsTrolleyPanel
IconDict = {ToontownGlobals.RaceGameId: 'mg_trolley_sign_race',
 ToontownGlobals.CannonGameId: 'mg_trolley_sign_cannon',
 ToontownGlobals.TagGameId: 'mg_trolley_sign_tag',
 ToontownGlobals.PatternGameId: 'mg_trolley_sign_minnie',
 ToontownGlobals.RingGameId: 'mg_trolley_sign_ring',
 ToontownGlobals.MazeGameId: 'mg_trolley_sign_maze',
 ToontownGlobals.TugOfWarGameId: 'mg_trolley_sign_tugawar',
 ToontownGlobals.CatchGameId: 'mg_trolley_sign_catch',
 ToontownGlobals.DivingGameId: 'mg_trolley_sign_dive',
 ToontownGlobals.TargetGameId: 'mg_trolley_sign_umbrella',
 ToontownGlobals.PairingGameId: 'mg_trolley_sign_card',
 ToontownGlobals.VineGameId: 'mg_trolley_sign_vine',
 ToontownGlobals.IceGameId: 'mg_trolley_sign_ice',
 ToontownGlobals.PhotoGameId: 'mg_trolley_sign_photo',
 ToontownGlobals.TwoDGameId: 'mg_trolley_sign_2d',
 ToontownGlobals.CogThiefGameId: 'mg_trolley_sign_theif'}
MinigameNameDict = {ToontownGlobals.RaceGameId: TTLocalizer.RaceGameTitle,
 ToontownGlobals.CannonGameId: TTLocalizer.CannonGameTitle,
 ToontownGlobals.TagGameId: TTLocalizer.TagGameTitle,
 ToontownGlobals.PatternGameId: TTLocalizer.PatternGameTitle,
 ToontownGlobals.RingGameId: TTLocalizer.RingGameTitle,
 ToontownGlobals.MazeGameId: TTLocalizer.MazeGameTitle,
 ToontownGlobals.TugOfWarGameId: TTLocalizer.TugOfWarGameTitle,
 ToontownGlobals.CatchGameId: TTLocalizer.CatchGameTitle,
 ToontownGlobals.DivingGameId: TTLocalizer.DivingGameTitle,
 ToontownGlobals.TargetGameId: TTLocalizer.TargetGameTitle,
 ToontownGlobals.PairingGameId: TTLocalizer.PairingGameTitle,
 ToontownGlobals.VineGameId: TTLocalizer.VineGameTitle,
 ToontownGlobals.TravelGameId: TTLocalizer.TravelGameTitle,
 ToontownGlobals.IceGameId: TTLocalizer.IceGameTitle,
 ToontownGlobals.PhotoGameId: TTLocalizer.PhotoGameTitle,
 ToontownGlobals.TwoDGameId: TTLocalizer.TwoDGameTitle,
 ToontownGlobals.CogThiefGameId: TTLocalizer.CogThiefGameTitle}

def makeLabel(itemName, itemNum, *extraArgs):
    intVersion = int(itemName)
    if intVersion < 0:
        textColor = Vec4(0, 0, 1, 1)
        intVersion = -intVersion
    elif intVersion == 0:
        textColor = Vec4(0, 0, 0, 1)
    else:
        textColor = Vec4(1, 0, 0, 1)
    return DirectLabel(text=str(intVersion), text_fg=textColor, relief=DGG.RIDGE, frameSize=(-1.2,
     1.2,
     -0.225,
     0.8), scale=1.0)


def map3dToAspect2d(node, point):
    p3 = base.cam.getRelativePoint(node, point)
    p2 = Point2()
    if not base.camLens.project(p3, p2):
        return None
    r2d = Point3(p2[0], 0, p2[1])
    a2d = aspect2d.getRelativePoint(render2d, r2d)
    return a2d


def invertTable(table):
    index = {}
    for key in table.keys():
        value = table[key]
        if not index.has_key(value):
            index[value] = key

    return index


class DistributedTravelGame(DistributedMinigame):
    notify = directNotify.newCategory('DistributedTravelGame')
    idToNames = MinigameNameDict
    TrolleyMoveDuration = 3
    UseTrolleyResultsPanel = True
    FlyCameraUp = True
    FocusOnTrolleyWhileMovingUp = False

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedTravelGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']),
         State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', 'displayVotes', 'cleanup']),
         State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['displayVotes', 'cleanup']),
         State.State('displayVotes', self.enterDisplayVotes, self.exitDisplayVotes, ['moveTrolley', 'cleanup']),
         State.State('moveTrolley', self.enterMoveTrolley, self.exitMoveTrolley, ['inputChoice', 'winMovie', 'cleanup']),
         State.State('winMovie', self.enterWinMovie, self.exitWinMovie, ['cleanup']),
         State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.currentVotes = {}
        self.cameraTopView = (100, -20, 280, 0, -89, 0)
        self.timer = None
        self.timerStartTime = None
        self.currentSwitch = 0
        self.destSwitch = 0
        self.minigameLabels = []
        self.minigameIcons = []
        self.bonusLabels = []
        self.trolleyAwaySfx = base.loader.loadSfx('phase_4/audio/sfx/SZ_trolley_away.mp3')
        self.trolleyBellSfx = base.loader.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.mp3')
        self.turntableRotateSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_turntble_rotate_2.mp3')
        self.wonGameSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_bonus.mp3')
        self.lostGameSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_no_bonus_2.mp3')
        self.noWinnerSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_no_bonus.mp3')
        self.boardIndex = 0
        self.avNames = []
        self.disconnectedAvIds = []
        return

    def getTitle(self):
        return TTLocalizer.TravelGameTitle

    def getInstructions(self):
        return TTLocalizer.TravelGameInstructions

    def getMaxDuration(self):
        return 0

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.sky = loader.loadModel('phase_3.5/models/props/TT_sky')
        self.gameBoard = loader.loadModel('phase_4/models/minigames/toon_cannon_gameground')
        self.gameBoard.setPosHpr(100, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        station = loader.loadModel('phase_4/models/modules/trolley_station_TT.bam')
        self.trolleyCar = station.find('**/trolley_car')
        self.trolleyCar.reparentTo(hidden)
        self.trolleyCarOrigPos = self.trolleyCar.getPos()
        self.trolleyCarOrigHpr = self.trolleyCar.getHpr()
        self.trolleyCar.setPosHpr(0, 0, 0, 0, 0, 0)
        self.trolleyCar.setScale(1.0)
        self.trolleyCar.setX(self.trolleyCar.getX() - TravelGameGlobals.xInc)
        station.removeNode()
        self.keys = self.trolleyCar.findAllMatches('**/key')
        self.numKeys = self.keys.getNumPaths()
        self.keyInit = []
        self.keyRef = []
        for i in range(self.numKeys):
            key = self.keys[i]
            key.setTwoSided(1)
            ref = self.trolleyCar.attachNewNode('key' + `i` + 'ref')
            ref.setPosHpr(key, 0, 0, 0, 0, 0, 0)
            self.keyRef.append(ref)
            self.keyInit.append(key.getTransform())

        self.frontWheels = self.trolleyCar.findAllMatches('**/front_wheels')
        self.numFrontWheels = self.frontWheels.getNumPaths()
        self.frontWheelInit = []
        self.frontWheelRef = []
        for i in range(self.numFrontWheels):
            wheel = self.frontWheels[i]
            ref = self.trolleyCar.attachNewNode('frontWheel' + `i` + 'ref')
            ref.setPosHpr(wheel, 0, 0, 0, 0, 0, 0)
            self.frontWheelRef.append(ref)
            self.frontWheelInit.append(wheel.getTransform())

        self.backWheels = self.trolleyCar.findAllMatches('**/back_wheels')
        self.numBackWheels = self.backWheels.getNumPaths()
        self.backWheelInit = []
        self.backWheelRef = []
        for i in range(self.numBackWheels):
            wheel = self.backWheels[i]
            ref = self.trolleyCar.attachNewNode('backWheel' + `i` + 'ref')
            ref.setPosHpr(wheel, 0, 0, 0, 0, 0, 0)
            self.backWheelRef.append(ref)
            self.backWheelInit.append(wheel.getTransform())

        trolleyAnimationReset = Func(self.resetAnimation)
        self.trainSwitches = {}
        self.trainTracks = {}
        self.tunnels = {}
        self.extraTrainTracks = []
        turnTable = loader.loadModel('phase_4/models/minigames/trolley_game_turntable')
        minPoint = Point3(0, 0, 0)
        maxPoint = Point3(0, 0, 0)
        turnTable.calcTightBounds(minPoint, maxPoint)
        self.fullLength = maxPoint[0]
        for key in TravelGameGlobals.BoardLayouts[self.boardIndex].keys():
            info = TravelGameGlobals.BoardLayouts[self.boardIndex][key]
            switchModel = turnTable.find('**/turntable1').copyTo(render)
            switchModel.setPos(*info['pos'])
            switchModel.reparentTo(hidden)
            self.trainSwitches[key] = switchModel
            zAdj = 0
            for otherSwitch in info['links']:
                info2 = TravelGameGlobals.BoardLayouts[self.boardIndex][otherSwitch]
                x1, y1, z1 = info['pos']
                x2, y2, z2 = info2['pos']
                linkKey = (key, otherSwitch)
                trainTrack = self.loadTrainTrack(x1, y1, x2, y2, zAdj)
                trainTrack.reparentTo(hidden)
                self.trainTracks[linkKey] = trainTrack
                zAdj += 0.005

        rootInfo = TravelGameGlobals.BoardLayouts[self.boardIndex][0]
        rootX, rootY, rootZ = rootInfo['pos']
        startX = rootX - TravelGameGlobals.xInc
        trainTrack = self.loadTrainTrack(startX, rootY, rootX, rootY)
        self.extraTrainTracks.append(trainTrack)
        tunnelX = None
        for key in TravelGameGlobals.BoardLayouts[self.boardIndex].keys():
            if self.isLeaf(key):
                info = TravelGameGlobals.BoardLayouts[self.boardIndex][key]
                switchX, switchY, switchZ = info['pos']
                endX = switchX + TravelGameGlobals.xInc
                trainTrack = self.loadTrainTrack(switchX, switchY, endX, switchY)
                self.extraTrainTracks.append(trainTrack)
                tempModel = loader.loadModel('phase_4/models/minigames/trolley_game_turntable')
                tunnel = tempModel.find('**/tunnel1')
                tunnel.reparentTo(render)
                tempModel.removeNode()
                if not tunnelX:
                    minTrackPoint = Point3(0, 0, 0)
                    maxTrackPoint = Point3(0, 0, 0)
                    trainTrack.calcTightBounds(minTrackPoint, maxTrackPoint)
                    tunnelX = maxTrackPoint[0]
                tunnel.setPos(tunnelX, switchY, 0)
                tunnel.wrtReparentTo(trainTrack)
                self.tunnels[key] = tunnel

        turnTable.removeNode()
        self.loadGui()
        self.introMovie = self.getIntroMovie()
        self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_Travel.mid')
        self.flashWinningBeansTrack = None
        return

    def loadTrainTrack(self, x1, y1, x2, y2, zAdj = 0):
        turnTable = loader.loadModel('phase_4/models/minigames/trolley_game_turntable')
        trainPart = turnTable.find('**/track_a2')
        trackHeight = 0.03
        trainTrack = render.attachNewNode('trainTrack%d%d%d%d' % (x1,
         y1,
         x2,
         y2))
        trainTrack.setPos(x1, y1, trackHeight)
        xDiff = abs(x2 - x1)
        yDiff = abs(y2 - y1)
        angleInRadians = math.atan((float(y2) - y1) / (x2 - x1))
        angle = rad2Deg(angleInRadians)
        desiredLength = math.sqrt(xDiff * xDiff + yDiff * yDiff)
        lengthToGo = desiredLength
        partIndex = 0
        lengthCovered = 0
        while lengthToGo > self.fullLength / 2.0:
            onePart = trainPart.copyTo(trainTrack)
            onePart.setX(lengthCovered)
            lengthToGo -= self.fullLength
            lengthCovered += self.fullLength

        trainTrack.setH(angle)
        newX = x1 + (x2 - x1) / 2.0
        newY = y1 + (y2 - y1) / 2.0
        trainTrack.setPos(x1, y1, trackHeight + zAdj)
        turnTable.removeNode()
        return trainTrack

    def loadGui(self):
        scoreText = [str(self.currentVotes[self.localAvId])]
        self.gui = DirectFrame()
        self.remainingVotesFrame = DirectFrame(parent=self.gui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(7, 1, 1), pos=(-0.9, 0, 0.8), scale=0.1, text=TTLocalizer.TravelGameRemainingVotes, text_align=TextNode.ALeft, text_scale=TTLocalizer.DTGremainingVotesFrame, text_pos=(-3.4, -0.1, 0.0))
        self.localVotesRemaining = DirectLabel(parent=self.remainingVotesFrame, relief=None, text=scoreText, text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(3.2, 0, -0.15))
        guiModel = loader.loadModel('phase_3.5/models/gui/friendslist_gui')
        self.choiceFrame = DirectFrame(parent=self.gui, relief=None, pos=(-0.55, 0, -0.85), image=DGG.getDefaultDialogGeom(), image_scale=(1.4, 1, 0.225), image_color=GlobalDialogColor)
        self.useLabel = DirectLabel(text=TTLocalizer.TravelGameUse, parent=self.choiceFrame, pos=(-0.59, 0, -0.01), text_scale=TTLocalizer.DTGuseLabel, relief=None)
        self.votesPeriodLabel = DirectLabel(text=TTLocalizer.TravelGameVotesWithPeriod, parent=self.choiceFrame, pos=(-0.21, 0, -0.01), text_scale=TTLocalizer.DTGvotesPeriodLabel, relief=None, text_align=TextNode.ALeft)
        self.votesToGoLabel = DirectLabel(text=TTLocalizer.TravelGameVotesToGo, parent=self.choiceFrame, pos=(-0.21, 0, -0.01), text_scale=TTLocalizer.DTGvotesToGoLabel, relief=None, text_align=TextNode.ALeft)
        self.upLabel = DirectLabel(text=TTLocalizer.TravelGameUp, parent=self.choiceFrame, pos=(0.31, 0, -0.01), text_scale=TTLocalizer.DTGupLabel, text_fg=Vec4(0, 0, 1, 1), relief=None, text_align=TextNode.ALeft)
        self.downLabel = DirectLabel(text=TTLocalizer.TravelGameDown, parent=self.choiceFrame, pos=(0.31, 0, -0.01), text_scale=TTLocalizer.DTGdownLabel, text_fg=Vec4(1, 0, 0, 1), relief=None, text_align=TextNode.ALeft)
        self.scrollList = DirectScrolledList(parent=self.choiceFrame, relief=None, pos=(-0.36, 0, -0.02), incButton_image=(guiModel.find('**/FndsLst_ScrollUp'),
         guiModel.find('**/FndsLst_ScrollDN'),
         guiModel.find('**/FndsLst_ScrollUp_Rllvr'),
         guiModel.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.04), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(guiModel.find('**/FndsLst_ScrollUp'),
         guiModel.find('**/FndsLst_ScrollDN'),
         guiModel.find('**/FndsLst_ScrollUp_Rllvr'),
         guiModel.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.095), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(0.0, 0.0, 0.0), itemFrame_relief=DGG.GROOVE, numItemsVisible=1, itemMakeFunction=makeLabel, items=[], scrollSpeed=3.0, itemFrame_scale=0.1, command=self.scrollChoiceChanged)
        self.putChoicesInScrollList()
        buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
        okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
        self.voteButton = DirectButton(parent=self.choiceFrame, relief=None, image=okImageList, image_scale=3.0, pos=(0.85, 0, 0.0), text=TTLocalizer.TravelGameVoteWithExclamation, text_scale=TTLocalizer.DTGvoteButton, text_pos=(0, 0), command=self.handleInputChoice)
        self.waitingChoicesLabel = DirectLabel(text=TTLocalizer.TravelGameWaitingChoices, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.2, 0, -0.85), scale=0.075)
        self.waitingChoicesLabel.hide()
        self.gui.hide()
        return

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        self.introMovie.finish()
        del self.introMovie
        self.gameBoard.removeNode()
        del self.gameBoard
        self.sky.removeNode()
        del self.sky
        self.trolleyCar.removeNode()
        del self.trolleyCar
        for key in self.trainSwitches.keys():
            self.trainSwitches[key].removeNode()
            del self.trainSwitches[key]

        self.trainSwitches = {}
        for key in self.tunnels.keys():
            self.tunnels[key].removeNode()
            del self.tunnels[key]

        self.tunnels = {}
        for key in self.trainTracks.keys():
            self.trainTracks[key].removeNode()
            del self.trainTracks[key]

        self.trainTracks = {}
        for trainTrack in self.extraTrainTracks:
            trainTrack.removeNode()
            del trainTrack

        self.extraTrainTracks = []
        self.gui.removeNode()
        del self.gui
        self.waitingChoicesLabel.destroy()
        del self.waitingChoicesLabel
        if self.flashWinningBeansTrack:
            self.flashWinningBeansTrack.finish()
            del self.flashWinningBeansTrack
        for label in self.minigameLabels:
            label.destroy()
            del label

        self.minigameLabels = []
        for icon in self.minigameIcons:
            icon.destroy()
            icon.removeNode()

        self.minigameIcons = []
        if hasattr(self, 'mg_icons'):
            del self.mg_icons
        for label in self.bonusLabels:
            label.destroy()
            del label

        self.bonusLabels = []
        self.scrollList.destroy()
        del self.scrollList
        self.voteButton.destroy()
        del self.voteButton
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        del self.music

    def moveCameraToTop(self):
        camera.reparentTo(render)
        p = self.cameraTopView
        camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5])

    def moveCameraToTrolley(self):
        camera.reparentTo(self.trolleyCar)
        camera.setPos(-25, 0, 7.5)
        camera.setHpr(-90, 0, 0)

    def onstage(self):
        self.notify.debug('onstage')
        NametagGlobals.setOnscreenChatForced(1)
        DistributedMinigame.onstage(self)
        self.gameBoard.reparentTo(render)
        self.sky.reparentTo(render)
        self.moveCameraToTop()
        self.trolleyCar.reparentTo(render)
        for key in self.trainSwitches.keys():
            self.trainSwitches[key].reparentTo(render)

        for key in self.trainTracks.keys():
            self.trainTracks[key].reparentTo(render)

        for trainTrack in self.extraTrainTracks:
            trainTrack.reparentTo(render)

        base.transitions.irisIn(0.4)
        base.setBackgroundColor(0.1875, 0.7929, 0)
        base.playMusic(self.music, looping=1, volume=0.9)
        self.introMovie.start()

    def offstage(self):
        self.notify.debug('offstage')
        NametagGlobals.setOnscreenChatForced(0)
        base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor)
        self.introMovie.finish()
        self.gameBoard.hide()
        self.sky.hide()
        self.trolleyCar.hide()
        self.gui.hide()
        self.hideMinigamesAndBonuses()
        for key in self.trainSwitches.keys():
            self.trainSwitches[key].hide()

        for key in self.trainTracks.keys():
            self.trainTracks[key].hide()

        for trainTrack in self.extraTrainTracks:
            trainTrack.hide()

        DistributedMinigame.offstage(self)
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.start()
        self.music.stop()

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        for index in range(self.numPlayers):
            avId = self.avIdList[index]
            name = ''
            avatar = self.getAvatar(avId)
            if avatar:
                avatar.reparentTo(self.trolleyCar)
                avatar.animFSM.request('Sit')
                avatar.setPosHpr(-4, -4.5 + index * 3, 2.8, 90, 0, 0)
                name = avatar.getName()
            self.avNames.append(name)

        self.trolleyCar.setH(90)

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        DistributedMinigame.setGameStart(self, timestamp)
        self.introMovie.finish()
        self.gameFSM.request('inputChoice')

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

    def exitOff(self):
        pass

    def enterInputChoice(self):
        self.notify.debug('enterInputChoice')
        NametagGlobals.setOnscreenChatForced(1)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.hide()
        if self.timerStartTime != None:
            self.startTimer()
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.stop()
        self.gui.show()
        self.showMinigamesAndBonuses()
        return

    def exitInputChoice(self):
        NametagGlobals.setOnscreenChatForced(0)
        if self.timer != None:
            self.timer.destroy()
            self.timer = None
        self.timerStartTime = None
        self.gui.hide()
        return

    def enterWaitServerChoices(self):
        self.notify.debug('enterWaitServerChoices')
        self.waitingChoicesLabel.show()
        self.gui.hide()

    def exitWaitServerChoices(self):
        self.waitingChoicesLabel.hide()

    def enterDisplayVotes(self, votes, directions, directionToGo, directionReason):
        if self.UseTrolleyResultsPanel:
            self.moveCameraToTrolley()
            self.hideMinigamesAndBonuses()
        else:
            self.moveCameraToTop()
        self.resultVotes = votes
        self.resultDirections = directions
        self.directionToGo = directionToGo
        self.directionReason = directionReason
        self.resultsStr = ''
        directionTotals = [0] * TravelGameGlobals.MaxDirections
        for index in range(len(votes)):
            if index < len(self.avNames):
                avId = self.avIdList[index]
                dir = directions[index]
                numVotes = votes[index]
                directionTotals[dir] += numVotes
                curStr = TTLocalizer.TravelGameOneToonVote % {'name': self.avNames[index],
                 'numVotes': numVotes,
                 'dir': TTLocalizer.TravelGameDirections[dir]}
                if not (numVotes == 0 and avId in self.disconnectedAvIds):
                    self.resultsStr += curStr

        directionStr = TTLocalizer.TravelGameTotals
        for index in range(len(directionTotals)):
            directionStr += ' ' + TTLocalizer.TravelGameDirections[index] + ':'
            directionStr += str(directionTotals[index])

        directionStr += '\n'
        self.resultsStr += directionStr
        reasonStr = ''
        if directionReason == TravelGameGlobals.ReasonVote:
            if directionToGo == 0:
                losingDirection = 1
            else:
                losingDirection = 0
            diffVotes = directionTotals[directionToGo] - directionTotals[losingDirection]
            reasonStr = ''
            if diffVotes > 1:
                reasonStr = TTLocalizer.TravelGameReasonVotesPlural % {'dir': TTLocalizer.TravelGameDirections[directionToGo],
                 'numVotes': diffVotes}
            else:
                reasonStr = TTLocalizer.TravelGameReasonVotesSingular % {'dir': TTLocalizer.TravelGameDirections[directionToGo],
                 'numVotes': diffVotes}
        elif directionReason == TravelGameGlobals.ReasonRandom:
            reasonStr = TTLocalizer.TravelGameReasonRandom % {'dir': TTLocalizer.TravelGameDirections[directionToGo],
             'numVotes': directionTotals[directionToGo]}
        elif directionReason == TravelGameGlobals.ReasonPlaceDecider:
            reasonStr = TravelGameReasonPlace % {'name': 'TODO NAME',
             'dir': TTLocalizer.TravelGameDirections[directionToGo]}
        self.resultsStr += reasonStr
        self.dialog = TTDialog.TTDialog(text=self.resultsStr, command=self.__cleanupDialog, style=TTDialog.NoButtons, pos=(0, 0, 1))
        self.dialog.hide()
        if self.UseTrolleyResultsPanel:
            self.votesPanel = VoteResultsTrolleyPanel.VoteResultsTrolleyPanel(len(self.avIdList), self.avIdList, votes, directions, self.avNames, self.disconnectedAvIds, directionToGo, directionReason, directionTotals)
        else:
            self.votesPanel = VoteResultsPanel.VoteResultsPanel(len(self.avIdList), self.avIdList, votes, directions, self.avNames, self.disconnectedAvIds, directionToGo, directionReason, directionTotals)
        self.votesPanel.startMovie()
        numPlayers = len(self.avIdList)
        if TravelGameGlobals.SpoofFour:
            numPlayers = 4
        delay = TravelGameGlobals.DisplayVotesTimePerPlayer * (numPlayers + 1)
        taskMgr.doMethodLater(delay, self.displayVotesTimeoutTask, self.taskName('displayVotes-timeout'))
        curSwitch = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch]
        self.destSwitch = curSwitch['links'][directionToGo]
        self.updateCurrentVotes()

    def exitDisplayVotes(self):
        taskMgr.remove(self.taskName('displayVotes-timeout'))
        self.__cleanupDialog(0)
        if not self.UseTrolleyResultsPanel:
            self.showMinigamesAndBonuses()
        self.votesPanel.destroy()

    def enterMoveTrolley(self):
        self.notify.debug('enterMoveTrolley')
        camera.wrtReparentTo(render)
        keyAngle = round(self.TrolleyMoveDuration) * 360
        dist = Vec3(self.trainSwitches[self.destSwitch].getPos() - self.trainSwitches[self.currentSwitch].getPos()).length()
        wheelAngle = dist / (2.0 * math.pi * 0.95) * 360
        trolleyAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=self.TrolleyMoveDuration, blendType='easeInOut', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate')
        moveTrolley = Sequence()
        moveTrolley.append(Func(self.resetAnimation))
        newPos = self.trainSwitches[self.destSwitch].getPos()
        linkKey = (self.currentSwitch, self.destSwitch)
        origHeading = self.trainTracks[linkKey].getH()
        heading = origHeading + 90
        firstTurn = Parallel()
        firstTurn.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0)))
        firstTurn.append(LerpHprInterval(self.trainSwitches[self.currentSwitch], 1, Vec3(origHeading, 0, 0)))
        firstTurn.append(LerpHprInterval(self.trainSwitches[self.destSwitch], 1, Vec3(origHeading, 0, 0)))
        moveTrolley.append(firstTurn)
        moveTrolley.append(Parallel(LerpPosInterval(self.trolleyCar, self.TrolleyMoveDuration, newPos, blendType='easeInOut'), trolleyAnimateInterval))
        secondTurn = Parallel()
        secondTurn.append(LerpHprInterval(self.trolleyCar, 1, Vec3(90, 0, 0)))
        secondTurn.append(LerpHprInterval(self.trainSwitches[self.currentSwitch], 1, Vec3(0, 0, 0)))
        secondTurn.append(LerpHprInterval(self.trainSwitches[self.destSwitch], 1, Vec3(0, 0, 0)))
        moveTrolley.append(secondTurn)
        soundTrack = Sequence()
        trolleyExitBellInterval = Parallel(SoundInterval(self.trolleyBellSfx, duration=1), SoundInterval(self.turntableRotateSfx, duration=1, volume=0.5))
        trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3)
        soundTrack.append(trolleyExitBellInterval)
        soundTrack.append(trolleyExitAwayInterval)
        soundTrack.append(trolleyExitBellInterval)
        self.moveTrolleyIval = Parallel(moveTrolley, soundTrack)
        duration = self.moveTrolleyIval.getDuration()

        def focusOnTrolley(t, self = self):
            pos = self.trolleyCar.getPos()
            pos.setZ(pos.getZ() + 7.5)
            camera.lookAt(pos)
            self.lastFocusHpr = camera.getHpr()

        setRightHprTime = 0
        if self.FlyCameraUp:
            setRightHprTime = 1.0
        camIval1 = Parallel()
        camIval1.append(LerpFunc(focusOnTrolley, duration - setRightHprTime, name='focusOnTrolley'))
        finalPos = Vec3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2])
        finalHpr = Vec3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5])
        if self.FlyCameraUp:
            if self.FocusOnTrolleyWhileMovingUp:
                camIval1.append(LerpPosInterval(camera, duration - setRightHprTime, finalPos, name='cameraMove'))
                camIval2 = Sequence(LerpHprInterval(camera, setRightHprTime, finalHpr, name='cameraHpr'))
            else:
                camIval2 = Sequence(LerpPosHprInterval(camera, setRightHprTime, finalPos, finalHpr, blendType='easeIn', name='cameraHpr'))
            camIval = Sequence(camIval1, camIval2)
        else:
            camIval = Sequence(camIval1)
        if self.UseTrolleyResultsPanel:
            self.moveTrolleyIval.append(camIval)
        temp = self.moveTrolleyIval
        self.moveTrolleyIval = Sequence(temp)
        if self.isLeaf(self.destSwitch):
            self.moveTrolleyIval.append(Func(self.gameFSM.request, 'winMovie'))
        else:
            self.moveTrolleyIval.append(Func(self.gameFSM.request, 'inputChoice'))
        self.moveTrolleyIval.start()

    def exitMoveTrolley(self):
        self.notify.debug('exitMoveTrolley')
        self.currentSwitch = self.destSwitch
        self.moveTrolleyIval.finish()
        self.moveCameraToTop()
        self.showMinigamesAndBonuses()

    def enterWinMovie(self):
        resultStr = TTLocalizer.TravelGamePlaying % {'game': self.idToNames[self.switchToMinigameDict[self.currentSwitch]]}
        numToons = 0
        for avId in self.avIdList:
            if avId not in self.disconnectedAvIds:
                numToons += 1

        if numToons <= 1:
            resultStr = TTLocalizer.TravelGameGoingBackToShop
        reachedGoalStr = None
        localAvatarWon = False
        localAvatarLost = False
        noWinner = True
        for avId in self.avIdBonuses.keys():
            name = ''
            avatar = self.getAvatar(avId)
            if avatar:
                name = avatar.getName()
                if self.avIdBonuses[avId][0] == self.currentSwitch:
                    noWinner = False
                    reachedGoalStr = TTLocalizer.TravelGameGotBonus % {'name': name,
                     'numBeans': self.avIdBonuses[avId][1]}
                    if avId == base.localAvatar.doId:
                        if not TravelGameGlobals.ReverseWin:
                            self.wonGameSfx.play()
                            bonusLabel = self.switchToBonusLabelDict[self.currentSwitch]
                            self.flashWinningBeansTrack = Sequence(LerpColorScaleInterval(bonusLabel, 0.75, Vec4(0.5, 1, 0.5, 1)), LerpColorScaleInterval(bonusLabel, 0.75, Vec4(1, 1, 1, 1)))
                            self.flashWinningBeansTrack.loop()
                        else:
                            self.lostGameSfx.play()
                    elif not TravelGameGlobals.ReverseWin:
                        self.lostGameSfx.play()
                    else:
                        self.wonGameSfx.play()

        if noWinner:
            self.noWinnerSfx.play()
            resultStr += '\n\n'
            resultStr += TTLocalizer.TravelGameNoOneGotBonus
        if reachedGoalStr:
            resultStr += '\n\n'
            resultStr += reachedGoalStr
        self.winDialog = TTDialog.TTDialog(text=resultStr, command=self.__cleanupWinDialog, style=TTDialog.NoButtons)
        info = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch]
        leafX, leafY, leafZ = info['pos']
        endX = leafX + TravelGameGlobals.xInc
        heading = 90
        moveTrolley = Sequence()
        moveTrolley.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0)))
        moveTrolley.append(LerpPosInterval(self.trolleyCar, 3, Vec3(endX + 20, leafY, 0)))
        soundTrack = Sequence()
        trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, duration=1)
        trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3)
        soundTrack.append(trolleyExitBellInterval)
        soundTrack.append(trolleyExitAwayInterval)
        soundTrack.append(trolleyExitBellInterval)
        self.moveTrolleyIval = Parallel(moveTrolley, soundTrack)
        self.moveTrolleyIval.start()
        delay = 8
        taskMgr.doMethodLater(delay, self.gameOverCallback, self.taskName('playMovie'))
        return

    def exitWinMovie(self):
        taskMgr.remove(self.taskName('playMovie'))
        self.moveTrolleyIval.finish()

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

    def exitCleanup(self):
        pass

    def setStartingVotes(self, startingVotesArray):
        if not len(startingVotesArray) == len(self.avIdList):
            self.notify.error('length does not match, startingVotes=%s, avIdList=%s' % (startingVotesArray, self.avIdList))
            return
        for index in range(len(self.avIdList)):
            avId = self.avIdList[index]
            self.startingVotes[avId] = startingVotesArray[index]
            if not self.currentVotes.has_key(avId):
                self.currentVotes[avId] = startingVotesArray[index]

        self.notify.debug('starting votes = %s' % self.startingVotes)

    def startTimer(self):
        now = globalClock.getFrameTime()
        elapsed = now - self.timerStartTime
        self.timer.setPos(1.16, 0, -0.83)
        self.timer.setTime(TravelGameGlobals.InputTimeout)
        self.timer.countdown(TravelGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout)
        self.timer.show()

    def setTimerStartTime(self, timestamp):
        if not self.hasLocalToon:
            return
        self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp)
        if self.timer != None:
            self.startTimer()
        return

    def handleChoiceTimeout(self):
        self.sendUpdate('setAvatarChoice', [0, 0])
        self.gameFSM.request('waitServerChoices')

    def putChoicesInScrollList(self):
        available = self.currentVotes[self.localAvId]
        if len(self.scrollList['items']) > 0:
            self.scrollList.removeAllItems()
        self.indexToVotes = {}
        index = 0
        for vote in range(available)[::-1]:
            self.scrollList.addItem(str(-(vote + 1)))
            self.indexToVotes[index] = vote + 1
            index += 1

        self.scrollList.addItem(str(0))
        self.indexToVotes[index] = 0
        self.zeroVoteIndex = index
        index += 1
        for vote in range(available):
            self.scrollList.addItem(str(vote + 1))
            self.indexToVotes[index] = vote + 1
            index += 1

        self.scrollList.scrollTo(self.zeroVoteIndex)

    def getAbsVoteChoice(self):
        available = self.currentVotes[self.localAvId]
        retval = 0
        if hasattr(self, 'scrollList'):
            selectedIndex = self.scrollList.getSelectedIndex()
            if self.indexToVotes.has_key(selectedIndex):
                retval = self.indexToVotes[selectedIndex]
        return retval

    def getAbsDirectionChoice(self):
        selectedIndex = self.scrollList.getSelectedIndex()
        if selectedIndex < self.zeroVoteIndex:
            retval = 0
        elif selectedIndex == self.zeroVoteIndex:
            retval = 0
        else:
            retval = 1
        return retval

    def makeTextMatchChoice(self):
        self.votesPeriodLabel.hide()
        self.votesToGoLabel.hide()
        self.upLabel.hide()
        self.downLabel.hide()
        if not hasattr(self, 'scrollList') or not hasattr(self, 'zeroVoteIndex'):
            return
        selectedIndex = self.scrollList.getSelectedIndex()
        if selectedIndex < self.zeroVoteIndex:
            self.votesToGoLabel.show()
            self.upLabel.show()
        elif selectedIndex == self.zeroVoteIndex:
            self.votesPeriodLabel.show()
        else:
            self.votesToGoLabel.show()
            self.downLabel.show()

    def scrollChoiceChanged(self):
        choiceVotes = self.getAbsVoteChoice()
        if choiceVotes == 1:
            self.votesToGoLabel['text'] = TTLocalizer.TravelGameVoteToGo
        else:
            self.votesToGoLabel['text'] = TTLocalizer.TravelGameVotesToGo
        available = self.currentVotes[self.localAvId]
        self.localVotesRemaining['text'] = str(available - choiceVotes)
        self.makeTextMatchChoice()

    def setAvatarChose(self, avId):
        if not self.hasLocalToon:
            return
        self.notify.debug('setAvatarChose: avatar: ' + str(avId) + ' choose a number')

    def handleInputChoice(self):
        numVotes = self.getAbsVoteChoice()
        direction = self.getAbsDirectionChoice()
        self.sendUpdate('setAvatarChoice', [numVotes, direction])
        self.gameFSM.request('waitServerChoices')

    def setServerChoices(self, votes, directions, directionToGo, directionReason):
        if not self.hasLocalToon:
            return
        self.notify.debug('requesting displayVotes, curState=%s' % self.gameFSM.getCurrentState().getName())
        self.gameFSM.request('displayVotes', [votes,
         directions,
         directionToGo,
         directionReason])

    def __cleanupDialog(self, value):
        if self.dialog:
            self.dialog.cleanup()
            self.dialog = None
        return

    def displayVotesTimeoutTask(self, task):
        self.notify.debug('Done waiting for display votes')
        self.gameFSM.request('moveTrolley')
        return Task.done

    def updateCurrentVotes(self):
        for index in range(len(self.resultVotes)):
            avId = self.avIdList[index]
            oldCurrentVotes = self.currentVotes[avId]
            self.currentVotes[avId] -= self.resultVotes[index]

        self.putChoicesInScrollList()
        self.makeTextMatchChoice()

    def isLeaf(self, switchIndex):
        retval = False
        links = TravelGameGlobals.BoardLayouts[self.boardIndex][switchIndex]['links']
        if len(links) == 0:
            retval = True
        return retval

    def __cleanupWinDialog(self, value):
        if hasattr(self, 'winDialog') and self.winDialog:
            self.winDialog.cleanup()
            self.winDialog = None
        return

    def gameOverCallback(self, task):
        self.__cleanupWinDialog(0)
        self.gameOver()
        return Task.done

    def setMinigames(self, switches, minigames):
        if not self.hasLocalToon:
            return
        self.switchToMinigameDict = {}
        for index in range(len(switches)):
            switch = switches[index]
            minigame = minigames[index]
            self.switchToMinigameDict[switch] = minigame

        self.notify.debug('minigameDict = %s' % self.switchToMinigameDict)
        self.loadMinigameIcons()

    def loadMinigameIcons(self):
        self.mg_icons = loader.loadModel('phase_4/models/minigames/mg_icons')
        for switch in self.switchToMinigameDict.keys():
            minigame = self.switchToMinigameDict[switch]
            switchPos = self.trainSwitches[switch].getPos()
            labelPos = map3dToAspect2d(render, switchPos)
            useText = True
            iconName = None
            if minigame in IconDict.keys():
                iconName = IconDict[minigame]
            icon = None
            if self.mg_icons:
                icon = self.mg_icons.find('**/%s' % iconName)
                if not icon.isEmpty():
                    useText = False
            if labelPos:
                if useText:
                    labelPos.setZ(labelPos.getZ() - 0.1)
                    label = DirectLabel(text=self.idToNames[minigame], relief=None, scale=0.1, pos=labelPos, text_fg=(1.0, 1.0, 1.0, 1.0))
                    label.hide()
                    self.minigameLabels.append(label)
                else:
                    placeHolder = DirectButton(image=icon, relief=None, text=('',
                     '',
                     self.idToNames[minigame],
                     ''), text_scale=0.3, text_pos=(0, -0.7, 0), text_fg=(1, 1, 1, 1), clickSound=None, pressEffect=0)
                    placeHolder.setPos(labelPos)
                    placeHolder.setScale(0.2)
                    placeHolder.hide()
                    self.minigameIcons.append(placeHolder)
                    tunnel = self.tunnels[switch]
                    sign = tunnel.attachNewNode('sign')
                    icon.copyTo(sign)
                    sign.setH(-90)
                    sign.setZ(26)
                    sign.setScale(10)

        return

    def showMinigamesAndBonuses(self):
        for label in self.minigameLabels:
            label.show()

        for label in self.bonusLabels:
            label.show()

        for icon in self.minigameIcons:
            icon.show()

    def hideMinigamesAndBonuses(self):
        for label in self.minigameLabels:
            label.hide()

        for label in self.bonusLabels:
            label.hide()

        for icon in self.minigameIcons:
            icon.hide()

    def loadBonuses(self):
        self.switchToBonusLabelDict = {}
        for avId in self.avIdBonuses.keys():
            if avId == self.localAvId:
                switch = self.avIdBonuses[avId][0]
                beans = self.avIdBonuses[avId][1]
                switchPos = self.trainSwitches[switch].getPos()
                labelPos = map3dToAspect2d(render, switchPos)
                if labelPos:
                    labelPos.setX(labelPos.getX() + 0.1)
                    labelPos.setZ(labelPos.getZ() - 0.02)
                    bonusStr = TTLocalizer.TravelGameBonusBeans % {'numBeans': beans}
                    label = DirectLabel(text=bonusStr, relief=None, scale=0.1, pos=labelPos, text_fg=(1.0, 1.0, 1.0, 1.0), text_align=TextNode.ALeft)
                    label.hide()
                    self.bonusLabels.append(label)
                    self.switchToBonusLabelDict[switch] = label
                break

        return

    def setBonuses(self, switches, beans):
        if not self.hasLocalToon:
            return
        self.avIdBonuses = {}
        for index in range(len(self.avIdList)):
            avId = self.avIdList[index]
            switch = switches[index]
            bean = beans[index]
            self.avIdBonuses[avId] = (switch, bean)

        self.notify.debug('self.avIdBonuses = %s' % self.avIdBonuses)
        self.loadBonuses()

    def handleDisabledAvatar(self, avId):
        self.notify.warning('DistrbutedTravelGame: handleDisabledAvatar: disabled avId: ' + str(avId))
        self.disconnectedAvIds.append(avId)

    def setBoardIndex(self, boardIndex):
        self.boardIndex = boardIndex

    def getIntroMovie(self):
        rootInfo = TravelGameGlobals.BoardLayouts[self.boardIndex][0]
        rootX, rootY, rootZ = rootInfo['pos']
        startX = rootX - TravelGameGlobals.xInc
        heading = 90
        moveTrolley = Sequence()
        moveTrolley.append(Func(self.trolleyCar.setH, 90))
        moveTrolley.append(LerpPosInterval(self.trolleyCar, 3, Vec3(rootX, rootY, 0), startPos=Vec3(startX, rootY, 0)))
        moveTrolley.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0)))
        soundTrack = Sequence()
        trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3)
        trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, duration=1)
        soundTrack.append(trolleyExitAwayInterval)
        soundTrack.append(trolleyExitBellInterval)
        retval = Parallel(moveTrolley, soundTrack)
        return retval

    def animateTrolley(self, t, keyAngle, wheelAngle):
        for i in range(self.numKeys):
            key = self.keys[i]
            ref = self.keyRef[i]
            key.setH(ref, t * keyAngle)

        for i in range(self.numFrontWheels):
            frontWheel = self.frontWheels[i]
            ref = self.frontWheelRef[i]
            frontWheel.setH(ref, t * wheelAngle)

        for i in range(self.numBackWheels):
            backWheel = self.backWheels[i]
            ref = self.backWheelRef[i]
            backWheel.setH(ref, t * wheelAngle)

    def resetAnimation(self):
        for i in range(self.numKeys):
            self.keys[i].setTransform(self.keyInit[i])

        for i in range(self.numFrontWheels):
            self.frontWheels[i].setTransform(self.frontWheelInit[i])

        for i in range(self.numBackWheels):
            self.backWheels[i].setTransform(self.backWheelInit[i])
