from panda3d.otp import *
from .PurchaseBase import *
from direct.task.Task import Task
from toontown.toon import ToonHead
from toontown.toonbase import ToontownTimer
from direct.gui import DirectGuiGlobals as DGG
from direct.directnotify import DirectNotifyGlobal
from direct.showbase.PythonUtil import Functor
from toontown.minigame import TravelGameGlobals
from toontown.distributed import DelayDelete
from toontown.toonbase import ToontownGlobals
from . import MinigameGlobals
COUNT_UP_RATE = 0.15
COUNT_UP_DURATION = 0.5
DELAY_BEFORE_COUNT_UP = 1.0
DELAY_AFTER_COUNT_UP = 1.0
COUNT_DOWN_RATE = 0.075
COUNT_DOWN_DURATION = 0.5
DELAY_AFTER_COUNT_DOWN = 0.0
DELAY_AFTER_CELEBRATE = 2.6
COUNT_SFX_MIN_DELAY = 0.034
COUNT_SFX_START_T = 0.079
OVERMAX_SFX_MIN_DELAY = 0.067
OVERMAX_SFX_START_T = 0.021

class Purchase(PurchaseBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('Purchase')

    def __init__(self, toon, pointsArray, playerMoney, ids, states, remain, doneEvent, metagameRound = -1, votesArray = None):
        PurchaseBase.__init__(self, toon, doneEvent)
        self.ids = ids
        self.pointsArray = pointsArray
        self.playerMoney = playerMoney
        self.states = states
        self.remain = remain
        self.tutorialMode = 0
        self.metagameRound = metagameRound
        self.votesArray = votesArray
        self.voteMultiplier = 1
        self.fsm.addState(State.State('reward', self.enterReward, self.exitReward, ['purchase']))
        doneState = self.fsm.getStateNamed('done')
        doneState.addTransition('reward')
        self.unexpectedEventNames = []
        self.unexpectedExits = []
        self.setupUnexpectedExitHooks()

    def load(self):
        purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui')
        PurchaseBase.load(self, purchaseModels)
        interiorPhase = 3.5
        self.bg = loader.loadModel('phase_%s/models/modules/toon_interior' % interiorPhase)
        self.bg.setPos(0.0, 5.0, -1.0)
        self.wt = self.bg.find('**/random_tc1_TI_wallpaper')
        wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.png' % interiorPhase)
        self.wt.setTexture(wallTex, 100)
        self.wt.setColorScale(0.8, 0.67, 0.549, 1.0)
        self.bt = self.bg.find('**/random_tc1_TI_wallpaper_border')
        wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.png' % interiorPhase)
        self.bt.setTexture(wallTex, 100)
        self.bt.setColorScale(0.8, 0.67, 0.549, 1.0)
        self.wb = self.bg.find('**/random_tc1_TI_wainscotting')
        wainTex = loader.loadTexture('phase_%s/maps/wall_paper_b4.png' % interiorPhase)
        self.wb.setTexture(wainTex, 100)
        self.wb.setColorScale(0.473, 0.675, 0.488, 1.0)
        self.playAgain = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.24), image=(purchaseModels.find('**/PurchScrn_BTN_UP'),
         purchaseModels.find('**/PurchScrn_BTN_DN'),
         purchaseModels.find('**/PurchScrn_BTN_RLVR'),
         purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopPlayAgain, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handlePlayAgain)
        self.backToPlayground = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.045), image=(purchaseModels.find('**/PurchScrn_BTN_UP'),
         purchaseModels.find('**/PurchScrn_BTN_DN'),
         purchaseModels.find('**/PurchScrn_BTN_RLVR'),
         purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopBackToPlayground, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handleBackToPlayground)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.reparentTo(self.frame)
        self.timer.posInTopRightCorner()
        numAvs = 0
        count = 0
        localToonIndex = 0
        for index in range(len(self.ids)):
            avId = self.ids[index]
            if avId == base.localAvatar.doId:
                localToonIndex = index
            if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE:
                numAvs = numAvs + 1

        layoutList = (None,
         (0,),
         (0, 2),
         (0, 1, 3),
         (0, 1, 2, 3))
        layout = layoutList[numAvs]
        headFramePosList = (Vec3(0.105, 0, -0.384),
         Vec3(0.105, 0, -0.776),
         Vec3(0.85, 0, -0.555),
         Vec3(-0.654, 0, -0.555))
        AVID_INDEX = 0
        LAYOUT_INDEX = 1
        TOON_INDEX = 2
        self.avInfoArray = [(base.localAvatar.doId, headFramePosList[0], localToonIndex)]
        pos = 1
        for index in range(len(self.ids)):
            avId = self.ids[index]
            if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE:
                if avId != base.localAvatar.doId:
                    if avId in base.cr.doId2do:
                        self.avInfoArray.append((avId, headFramePosList[layout[pos]], index))
                        pos = pos + 1

        self.headFrames = []
        for avInfo in self.avInfoArray:
            av = base.cr.doId2do.get(avInfo[AVID_INDEX])
            if av:
                headFrame = PurchaseHeadFrame(av, purchaseModels)
                headFrame.setAvatarState(self.states[avInfo[TOON_INDEX]])
                headFrame.setPos(avInfo[LAYOUT_INDEX])
                self.headFrames.append((avInfo[AVID_INDEX], headFrame))

        purchaseModels.removeNode()
        self.foreground = loader.loadModel('phase_3.5/models/modules/TT_A1')
        self.foreground.setPos(12.5, -20, -5.5)
        self.foreground.setHpr(180, 0, 0)
        self.backgroundL = loader.loadModel('phase_3.5/models/modules/TT_A1')
        self.backgroundL.setPos(-12.5, -25, -5)
        self.backgroundL.setHpr(180, 0, 0)
        self.backgroundR = self.backgroundL.copyTo(hidden)
        self.backgroundR.setPos(20, -25, -5)
        streets = loader.loadModel('phase_3.5/models/modules/street_modules')
        sidewalk = streets.find('**/street_sidewalk_40x40')
        self.sidewalk = sidewalk.copyTo(hidden)
        self.sidewalk.setPos(-20, -25, -5.5)
        self.sidewalk.setColor(0.9, 0.6, 0.4)
        streets.removeNode()
        doors = loader.loadModel('phase_4/models/modules/doors')
        door = doors.find('**/door_single_square_ur_door')
        self.door = door.copyTo(hidden)
        self.door.setH(180)
        self.door.setPos(0, -16.75, -5.5)
        self.door.setScale(1.5, 1.5, 2.0)
        self.door.setColor(1.0, 0.8, 0, 1)
        doors.removeNode()
        self.convertingVotesToBeansLabel = DirectLabel(text=TTLocalizer.TravelGameConvertingVotesToBeans, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(0.0, 0, -0.58), scale=0.075)
        self.convertingVotesToBeansLabel.hide()
        self.rewardDoubledJellybeanLabel = DirectLabel(text=TTLocalizer.PartyRewardDoubledJellybean, text_fg=(1.0, 0.125, 0.125, 1.0), text_shadow=(0, 0, 0, 1), relief=None, pos=(0.0, 0, -0.67), scale=0.08)
        self.rewardDoubledJellybeanLabel.hide()
        self.countSound = base.loader.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg')
        self.overMaxSound = base.loader.loadSfx('phase_3.5/audio/sfx/AV_collision.ogg')
        self.celebrateSound = base.loader.loadSfx('phase_4/audio/sfx/MG_win.ogg')
        return

    def unload(self):
        PurchaseBase.unload(self)
        self.cleanupUnexpectedExitHooks()
        self.bg.removeNode()
        del self.bg
        self.notify.debug('destroying head frames')
        for headFrame in self.headFrames:
            if not headFrame[1].isEmpty():
                headFrame[1].reparentTo(hidden)
                headFrame[1].destroy()

        del self.headFrames
        self.playAgain.destroy()
        del self.playAgain
        self.backToPlayground.destroy()
        del self.backToPlayground
        self.timer.stop()
        del self.timer
        for counter in self.counters:
            counter.destroy()
            del counter

        del self.counters
        for total in self.totalCounters:
            total.destroy()
            del total

        del self.totalCounters
        loader.unloadModel('phase_3.5/models/modules/TT_A1')
        loader.unloadModel('phase_3.5/models/modules/street_modules')
        loader.unloadModel('phase_4/models/modules/doors')
        taskMgr.remove('countUpTask')
        taskMgr.remove('countVotesUpTask')
        taskMgr.remove('countDownTask')
        taskMgr.remove('countVotesDownTask')
        taskMgr.remove('celebrate')
        taskMgr.remove('purchase-trans')
        taskMgr.remove('delayAdd')
        taskMgr.remove('delaySubtract')
        self.foreground.removeNode()
        del self.foreground
        self.backgroundL.removeNode()
        del self.backgroundL
        self.backgroundR.removeNode()
        del self.backgroundR
        self.sidewalk.removeNode()
        del self.sidewalk
        self.door.removeNode()
        del self.door
        self.collisionFloor.removeNode()
        del self.collisionFloor
        del self.countSound
        del self.celebrateSound
        self.convertingVotesToBeansLabel.removeNode()
        self.rewardDoubledJellybeanLabel.removeNode()
        del self.convertingVotesToBeansLabel
        del self.rewardDoubledJellybeanLabel

    def showStatusText(self, text):
        self.statusLabel['text'] = text
        taskMgr.remove('resetStatusText')
        taskMgr.doMethodLater(2.0, self.resetStatusText, 'resetStatusText')

    def resetStatusText(self, task):
        self.statusLabel['text'] = ''
        return Task.done

    def __handlePlayAgain(self):
        for headFrame in self.headFrames:
            headFrame[1].wrtReparentTo(aspect2d)

        self.toon.inventory.reparentTo(hidden)
        self.toon.inventory.hide()
        taskMgr.remove('resetStatusText')
        taskMgr.remove('showBrokeMsgTask')
        self.statusLabel['text'] = TTLocalizer.GagShopWaitingOtherPlayers
        messenger.send('purchasePlayAgain')

    def handleDone(self, playAgain):
        base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
        if playAgain:
            self.doneStatus = {'loader': 'minigame',
             'where': 'minigame'}
        else:
            self.doneStatus = {'loader': 'safeZoneLoader',
             'where': 'playground'}
        messenger.send(self.doneEvent)

    def __handleBackToPlayground(self):
        self.toon.inventory.reparentTo(hidden)
        self.toon.inventory.hide()
        messenger.send('purchaseBackToToontown')

    def __timerExpired(self):
        messenger.send('purchaseTimeout')

    def findHeadFrame(self, id):
        for headFrame in self.headFrames:
            if headFrame[0] == id:
                return headFrame[1]

        return None

    def __handleStateChange(self, playerStates):
        self.states = playerStates
        for avInfo in self.avInfoArray:
            index = avInfo[2]
            headFrame = self.findHeadFrame(avInfo[0])
            state = self.states[index]
            headFrame.setAvatarState(state)

    def enter(self):
        base.playMusic(self.music, looping=1, volume=0.8)
        self.fsm.request('reward')

    def enterReward(self):
        numToons = 0
        toonLayouts = ((2,),
         (1, 3),
         (0, 2, 4),
         (0, 1, 3, 4))
        toonPositions = (5.0,
         1.75,
         -0.25,
         -1.75,
         -5.0)
        self.toons = []
        self.toonsKeep = []
        self.counters = []
        self.totalCounters = []
        camera.reparentTo(render)
        base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
        camera.setPos(0, 16.0, 2.0)
        camera.lookAt(0, 0, 0.75)
        base.transitions.irisIn(0.4)
        self.title.reparentTo(aspect2d)
        self.foreground.reparentTo(render)
        self.backgroundL.reparentTo(render)
        self.backgroundR.reparentTo(render)
        self.sidewalk.reparentTo(render)
        self.door.reparentTo(render)
        size = 20
        z = -2.5
        floor = CollisionPolygon(Point3(-size, -size, z), Point3(size, -size, z), Point3(size, size, z), Point3(-size, size, z))
        floor.setTangible(1)
        floorNode = CollisionNode('collision_floor')
        floorNode.addSolid(floor)
        self.collisionFloor = render.attachNewNode(floorNode)
        NametagGlobals.setOnscreenChatForced(1)
        for index in range(len(self.ids)):
            avId = self.ids[index]
            if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE and avId in base.cr.doId2do:
                numToons += 1
                toon = base.cr.doId2do[avId]
                toon.stopSmooth()
                self.toons.append(toon)
                self.toonsKeep.append(DelayDelete.DelayDelete(toon, 'Purchase.enterReward'))
                counter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(0), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont())
                counter['image'] = DGG.getDefaultDialogGeom()
                counter['image_scale'] = (0.33, 1, 0.33)
                counter.setScale(0.5)
                counter.count = 0
                counter.max = self.pointsArray[index]
                self.counters.append(counter)
                money = self.playerMoney[index]
                totalCounter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(money), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont(), image=self.jarImage)
                totalCounter.setScale(0.5)
                totalCounter.count = money
                totalCounter.max = toon.getMaxMoney()
                self.totalCounters.append(totalCounter)

        self.accept('clientCleanup', self._handleClientCleanup)
        pos = 0
        toonLayout = toonLayouts[numToons - 1]
        for toon in self.toons:
            thisPos = toonPositions[toonLayout[pos]]
            toon.setPos(Vec3(thisPos, 1.0, -2.5))
            toon.setHpr(Vec3(0, 0, 0))
            toon.setAnimState('neutral', 1)
            toon.setShadowHeight(0)
            if not toon.isDisabled():
                toon.reparentTo(render)
            self.counters[pos].setPos(thisPos * -0.17, 0, toon.getHeight() / 10 + 0.25)
            self.counters[pos].reparentTo(aspect2d)
            self.totalCounters[pos].setPos(thisPos * -0.17, 0, -0.825)
            self.totalCounters[pos].reparentTo(aspect2d)
            pos += 1

        self.maxPoints = max(self.pointsArray)
        if self.votesArray:
            self.maxVotes = max(self.votesArray)
            numToons = len(self.toons)
            self.voteMultiplier = TravelGameGlobals.PercentOfVotesConverted[numToons] / 100.0
            self.maxBeansFromVotes = int(self.voteMultiplier * self.maxVotes)
        else:
            self.maxVotes = 0
            self.maxBeansFromVotes = 0

        def reqCountUp(state):
            self.countUp()
            return Task.done

        countUpDelay = DELAY_BEFORE_COUNT_UP
        taskMgr.doMethodLater(countUpDelay, reqCountUp, 'countUpTask')

        def reqCountDown(state):
            self.countDown()
            return Task.done

        countDownDelay = countUpDelay + COUNT_UP_DURATION + DELAY_AFTER_COUNT_UP
        taskMgr.doMethodLater(countDownDelay, reqCountDown, 'countDownTask')

        def celebrate(task):
            for counter in task.counters:
                counter.hide()

            winningPoints = max(task.pointsArray)
            for i in range(len(task.ids)):
                if task.pointsArray[i] == winningPoints:
                    avId = task.ids[i]
                    if avId in base.cr.doId2do:
                        toon = base.cr.doId2do[avId]
                        toon.setAnimState('jump', 1.0)

            base.playSfx(task.celebrateSound)
            return Task.done

        celebrateDelay = countDownDelay + COUNT_DOWN_DURATION + DELAY_AFTER_COUNT_DOWN
        celebrateTask = taskMgr.doMethodLater(celebrateDelay, celebrate, 'celebrate')
        celebrateTask.counters = self.counters
        celebrateTask.pointsArray = self.pointsArray
        celebrateTask.ids = self.ids
        celebrateTask.celebrateSound = self.celebrateSound

        def reqCountVotesUp(state):
            self.countVotesUp()
            return Task.done

        def reqCountVotesDown(state):
            self.countVotesDown()
            return Task.done

        if self.metagameRound == TravelGameGlobals.FinalMetagameRoundIndex:
            countVotesUpDelay = celebrateDelay + DELAY_AFTER_CELEBRATE
            taskMgr.doMethodLater(countVotesUpDelay, reqCountVotesUp, 'countVotesUpTask')
            countVotesUpTime = self.maxVotes * COUNT_UP_RATE + DELAY_AFTER_COUNT_UP
            countVotesDownDelay = countVotesUpDelay + countVotesUpTime
            taskMgr.doMethodLater(countVotesDownDelay, reqCountVotesDown, 'countVotesDownTask')
            celebrateDelay += countVotesUpTime + self.maxVotes * COUNT_DOWN_RATE + DELAY_AFTER_COUNT_DOWN

        def reqPurchase(state):
            self.fsm.request('purchase')
            return Task.done

        purchaseDelay = celebrateDelay + DELAY_AFTER_CELEBRATE
        taskMgr.doMethodLater(purchaseDelay, reqPurchase, 'purchase-trans')
        if base.skipMinigameReward:
            self.fsm.request('purchase')
        return

    def _changeCounterUp(self, task, counter, newCount, toonId):
        counter.count = newCount
        counter['text'] = str(counter.count)
        if toonId == base.localAvatar.doId:
            now = globalClock.getRealTime()
            if task.lastSfxT + COUNT_SFX_MIN_DELAY < now:
                base.playSfx(task.countSound, time=COUNT_SFX_START_T)
                task.lastSfxT = now

    def _countUpTask(self, task):
        now = globalClock.getRealTime()
        startT = task.getStartTime()
        if now >= startT + task.duration:
            for counter, toonId in zip(self.counters, self.ids):
                if counter.count != counter.max:
                    self._changeCounterUp(task, counter, counter.max, toonId)

            return Task.done
        t = (now - startT) / task.duration
        for counter, toonId in zip(self.counters, self.ids):
            curCount = int(triglerp(0, counter.max, t))
            if curCount != counter.count:
                self._changeCounterUp(task, counter, curCount, toonId)

        return Task.cont

    def countUp(self):
        totalDelay = 0
        if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH):
            self.rewardDoubledJellybeanLabel.show()
        countUpTask = taskMgr.add(self._countUpTask, 'countUp')
        countUpTask.duration = COUNT_UP_DURATION
        countUpTask.countSound = self.countSound
        countUpTask.lastSfxT = 0

    def _changeCounterDown(self, task, counter, newCount, total, toonId):
        counter.count = newCount
        counter['text'] = str(counter.count)
        total.count = total.startAmount + (counter.max - newCount)
        if total.count > total.max:
            total.count = total.max
        total['text'] = str(total.count)
        if total.count == total.max:
            total['text_fg'] = (1, 0, 0, 1)
        if toonId == base.localAvatar.doId:
            now = globalClock.getRealTime()
            if total.count < total.max:
                minDelay = COUNT_SFX_MIN_DELAY
                snd = task.countSound
                startT = COUNT_SFX_START_T
            else:
                minDelay = OVERMAX_SFX_MIN_DELAY
                snd = task.overMaxSound
                startT = OVERMAX_SFX_START_T
            if task.lastSfxT + minDelay < now:
                task.lastSfxT = now
                base.playSfx(snd, time=startT)

    def _countDownTask(self, task):
        now = globalClock.getRealTime()
        startT = task.getStartTime()
        if now >= startT + task.duration:
            for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids):
                if counter.count != 0:
                    self._changeCounterDown(task, counter, 0, total, toonId)

            return Task.done
        t = (now - startT) / task.duration
        for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids):
            curCount = int(triglerp(counter.max, 0, t))
            if curCount != counter.count:
                self._changeCounterDown(task, counter, curCount, total, toonId)

        return Task.cont

    def countDown(self):
        totalDelay = 0
        for total in self.totalCounters:
            total.startAmount = total.count

        countDownTask = taskMgr.add(self._countDownTask, 'countDown')
        countDownTask.duration = COUNT_DOWN_DURATION
        countDownTask.countSound = self.countSound
        countDownTask.overMaxSound = self.overMaxSound
        countDownTask.lastSfxT = 0

    def countVotesUp(self):
        totalDelay = 0
        self.convertingVotesToBeansLabel.show()
        if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH):
            self.rewardDoubledJellybeanLabel.show()
        counterIndex = 0
        for index in range(len(self.ids)):
            avId = self.ids[index]
            if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE and avId in base.cr.doId2do:
                self.counters[counterIndex].count = 0
                self.counters[counterIndex].max = self.votesArray[index]
                self.counters[counterIndex].show()
                counterIndex += 1

        def delayAdd(state):
            state.counter.count += 1
            state.counter['text'] = str(state.counter.count)
            if state.toonId == base.localAvatar.doId:
                base.playSfx(state.countSound)
            return Task.done

        for count in range(0, self.maxVotes):
            for counter in self.counters:
                index = self.counters.index(counter)
                if count < counter.max:
                    addTask = taskMgr.doMethodLater(totalDelay, delayAdd, 'delayAdd')
                    addTask.counter = counter
                    addTask.toonId = self.ids[index]
                    addTask.countSound = self.countSound

            totalDelay += COUNT_UP_RATE

    def countVotesDown(self):
        totalDelay = 0

        def delaySubtract(state):
            state.counter.count -= 1
            state.counter['text'] = str(state.counter.count)
            state.total.count += 1 * self.voteMultiplier
            if state.total.count <= state.total.max:
                state.total['text'] = str(int(state.total.count))
            if state.total.count == state.total.max + 1:
                state.total['text_fg'] = (1, 0, 0, 1)
            if state.toonId == base.localAvatar.doId:
                if state.total.count <= state.total.max:
                    base.playSfx(state.countSound)
                else:
                    base.playSfx(state.overMaxSound)
            return Task.done

        for count in range(0, self.maxVotes):
            for counter in self.counters:
                if count < counter.max:
                    index = self.counters.index(counter)
                    subtractTask = taskMgr.doMethodLater(totalDelay, delaySubtract, 'delaySubtract')
                    subtractTask.counter = counter
                    subtractTask.total = self.totalCounters[index]
                    subtractTask.toonId = self.ids[index]
                    subtractTask.countSound = self.countSound
                    subtractTask.overMaxSound = self.overMaxSound

            totalDelay += COUNT_DOWN_RATE

    def exitReward(self):
        self.ignore('clientCleanup')
        taskMgr.remove('countUpTask')
        taskMgr.remove('countVotesUpTask')
        taskMgr.remove('countDownTask')
        taskMgr.remove('countVotesDownTask')
        taskMgr.remove('celebrate')
        taskMgr.remove('purchase-trans')
        taskMgr.remove('delayAdd')
        taskMgr.remove('delaySubtract')
        for toon in self.toons:
            toon.detachNode()

        del self.toons
        if hasattr(self, 'toonsKeep'):
            for delayDelete in self.toonsKeep:
                delayDelete.destroy()

            del self.toonsKeep
        for counter in self.counters:
            counter.reparentTo(hidden)

        for total in self.totalCounters:
            total.reparentTo(hidden)

        self.foreground.reparentTo(hidden)
        self.backgroundL.reparentTo(hidden)
        self.backgroundR.reparentTo(hidden)
        self.sidewalk.reparentTo(hidden)
        self.door.reparentTo(hidden)
        self.title.reparentTo(self.frame)
        self.convertingVotesToBeansLabel.hide()
        self.rewardDoubledJellybeanLabel.hide()
        NametagGlobals.setOnscreenChatForced(0)

    def _handleClientCleanup(self):
        if hasattr(self, 'toonsKeep'):
            for delayDelete in self.toonsKeep:
                delayDelete.destroy()

            del self.toonsKeep
        self.ignore('clientCleanup')

    def enterPurchase(self):
        PurchaseBase.enterPurchase(self)
        self.convertingVotesToBeansLabel.hide()
        self.rewardDoubledJellybeanLabel.hide()
        self.bg.reparentTo(render)
        base.setBackgroundColor(0.05, 0.14, 0.4)
        self.accept('purchaseStateChange', self.__handleStateChange)
        self.playAgain.reparentTo(self.toon.inventory.purchaseFrame)
        self.backToPlayground.reparentTo(self.toon.inventory.purchaseFrame)
        self.pointDisplay.reparentTo(self.toon.inventory.purchaseFrame)
        self.statusLabel.reparentTo(self.toon.inventory.purchaseFrame)
        for headFrame in self.headFrames:
            headFrame[1].show()
            headFrame[1].reparentTo(self.toon.inventory.purchaseFrame)

        if base.cr.periodTimerExpired:
            base.cr.loginFSM.request('periodTimeout')
            return
        if not self.tutorialMode:
            if not config.GetBool('disable-purchase-timer', 0):
                self.timer.countdown(self.remain, self.__timerExpired)
            if config.GetBool('metagame-disable-playAgain', 0):
                if self.metagameRound > -1:
                    self.disablePlayAgain()
        else:
            self.timer.hide()
            self.disablePlayAgain()
            self.accept('disableGagPanel', Functor(self.toon.inventory.setActivateMode, 'gagTutDisabled', gagTutMode=1))
            self.accept('disableBackToPlayground', self.disableBackToPlayground)
            self.accept('enableGagPanel', self.handleEnableGagPanel)
            self.accept('enableBackToPlayground', self.enableBackToPlayground)
            for avId, headFrame in self.headFrames:
                if avId != self.newbieId:
                    headFrame.hide()

        messenger.send('gagScreenIsUp')
        if base.autoPlayAgain or self.doMetagamePlayAgain():
            base.transitions.fadeOut(0)
            self.__handlePlayAgain()

    def exitPurchase(self):
        PurchaseBase.exitPurchase(self)
        self.ignore('disableGagPanel')
        self.ignore('disableBackToPlayground')
        self.ignore('enableGagPanel')
        self.ignore('enableBackToPlayground')
        self.bg.reparentTo(hidden)
        self.playAgain.reparentTo(self.frame)
        self.backToPlayground.reparentTo(self.frame)
        self.pointDisplay.reparentTo(self.frame)
        self.statusLabel.reparentTo(self.frame)
        self.ignore('purchaseStateChange')
        base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor)
        if base.autoPlayAgain or self.doMetagamePlayAgain():
            base.transitions.fadeIn()

    def disableBackToPlayground(self):
        self.backToPlayground['state'] = DGG.DISABLED

    def enableBackToPlayground(self):
        self.backToPlayground['state'] = DGG.NORMAL

    def disablePlayAgain(self):
        self.playAgain['state'] = DGG.DISABLED

    def enablePlayAgain(self):
        self.playAgain['state'] = DGG.NORMAL

    def enterTutorialMode(self, newbieId):
        self.tutorialMode = 1
        self.newbieId = newbieId

    def handleEnableGagPanel(self):
        self.toon.inventory.setActivateMode('purchase', gagTutMode=1)
        self.checkForBroke()

    def handleGagTutorialDone(self):
        self.enableBackToPlayground()

    def doMetagamePlayAgain(self):
        if hasattr(self, 'metagamePlayAgainResult'):
            return self.metagamePlayAgainResult
        numToons = 0
        for avId in self.ids:
            if avId in base.cr.doId2do and avId not in self.unexpectedExits:
                numToons += 1

        self.metagamePlayAgainResult = False
        if numToons > 1:
            if self.metagameRound > -1 and self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex:
                self.metagamePlayAgainResult = True
        return self.metagamePlayAgainResult

    def setupUnexpectedExitHooks(self):
        for avId in self.ids:
            if avId in base.cr.doId2do:
                toon = base.cr.doId2do[avId]
                eventName = toon.uniqueName('disable')
                self.accept(eventName, self.__handleUnexpectedExit, extraArgs=[avId])
                self.unexpectedEventNames.append(eventName)

    def cleanupUnexpectedExitHooks(self):
        for eventName in self.unexpectedEventNames:
            self.ignore(eventName)

    def __handleUnexpectedExit(self, avId):
        self.unexpectedExits.append(avId)


class PurchaseHeadFrame(DirectFrame):
    notify = DirectNotifyGlobal.directNotify.newCategory('Purchase')

    def __init__(self, av, purchaseModels):
        DirectFrame.__init__(self, relief=None, image=purchaseModels.find('**/Char_Pnl'))
        self.initialiseoptions(PurchaseHeadFrame)
        self.statusLabel = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.PstatusLabel, text_wordwrap=7.5, text_fg=(0.05, 0.14, 0.4, 1), text_pos=(0.1, 0, 0))
        self.av = av
        self.avKeep = DelayDelete.DelayDelete(av, 'PurchaseHeadFrame.av')
        self.accept('clientCleanup', self._handleClientCleanup)
        self.head = self.stateNodePath[0].attachNewNode('head', 20)
        self.head.setPosHprScale(-0.22, 10.0, -0.1, 180.0, 0.0, 0.0, 0.1, 0.1, 0.1)
        self.headModel = ToonHead.ToonHead()
        self.headModel.setupHead(self.av.style, forGui=1)
        self.headModel.reparentTo(self.head)
        self.tag2Node = NametagFloat2d()
        self.tag2Node.setContents(Nametag.CName)
        self.av.nametag.addNametag(self.tag2Node)
        self.tag2 = self.attachNewNode(self.tag2Node.upcastToPandaNode())
        self.tag2.setPosHprScale(-0.22, 10.0, 0.12, 0, 0, 0, 0.046, 0.046, 0.046)
        self.tag1Node = NametagFloat2d()
        self.tag1Node.setContents(Nametag.CSpeech | Nametag.CThought)
        self.av.nametag.addNametag(self.tag1Node)
        self.tag1 = self.attachNewNode(self.tag1Node.upcastToPandaNode())
        self.tag1.setPosHprScale(-0.15, 0, -0.1, 0, 0, 0, 0.046, 0.046, 0.046)
        self.hide()
        return

    def destroy(self):
        DirectFrame.destroy(self)
        del self.statusLabel
        self.headModel.delete()
        del self.headModel
        self.head.removeNode()
        del self.head
        self.av.nametag.removeNametag(self.tag1Node)
        self.av.nametag.removeNametag(self.tag2Node)
        self.tag1.removeNode()
        self.tag2.removeNode()
        del self.tag1
        del self.tag2
        del self.tag1Node
        del self.tag2Node
        del self.av
        self.removeAvKeep()

    def setAvatarState(self, state):
        if state == PURCHASE_DISCONNECTED_STATE:
            self.statusLabel['text'] = TTLocalizer.GagShopPlayerDisconnected % self.av.getName()
            self.statusLabel['text_pos'] = (0.015, 0.072, 0)
            self.head.hide()
            self.tag1.hide()
            self.tag2.hide()
        elif state == PURCHASE_EXIT_STATE:
            self.statusLabel['text'] = TTLocalizer.GagShopPlayerExited % self.av.getName()
            self.statusLabel['text_pos'] = (0.015, 0.072, 0)
            self.head.hide()
            self.tag1.hide()
            self.tag2.hide()
        elif state == PURCHASE_PLAYAGAIN_STATE:
            self.statusLabel['text'] = TTLocalizer.GagShopPlayerPlayAgain
            self.statusLabel['text_pos'] = (0.1, -0.12, 0)
        elif state == PURCHASE_WAITING_STATE:
            self.statusLabel['text'] = TTLocalizer.GagShopPlayerBuying
            self.statusLabel['text_pos'] = (0.1, -0.12, 0)
        elif state == PURCHASE_NO_CLIENT_STATE:
            Purchase.notify.warning("setAvatarState('no client state'); OK for gag purchase tutorial")
        else:
            Purchase.notify.warning('unknown avatar state: %s' % state)

    def _handleClientCleanup(self):
        self.destroy()

    def removeAvKeep(self):
        if hasattr(self, 'avKeep'):
            self.notify.debug('destroying avKeep %s' % self.avKeep)
            self.avKeep.destroy()
            del self.avKeep
        self.ignore('clientCleanup')