from panda3d.core import *
from toontown.toonbase.ToonBaseGlobal import *
from DistributedMinigame import *
from direct.interval.IntervalGlobal import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from toontown.safezone import Walk, SnowUtil
from toontown.toonbase import ToontownTimer
from direct.gui import OnscreenText
import MinigameAvatarScorePanel
from direct.distributed import DistributedSmoothNode
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
from otp.otpbase import OTPGlobals
import TagGameGlobals
import Trajectory

class DistributedTagGame(DistributedMinigame):
    DURATION = TagGameGlobals.DURATION
    IT_SPEED_INCREASE = 1.3
    IT_ROT_INCREASE = 1.3

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedTagGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off')
        self.addChildGameFSM(self.gameFSM)
        self.walkStateData = Walk.Walk('walkDone')
        self.scorePanels = []
        base.localAvatar.isIt = 0
        self.modelCount = 4

    def getTitle(self):
        return TTLocalizer.TagGameTitle

    def getInstructions(self):
        return TTLocalizer.TagGameInstructions

    def getMaxDuration(self):
        return self.DURATION

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.itText = OnscreenText.OnscreenText('itText', fg=(0.95, 0.95, 0.65, 1), scale=0.14, font=ToontownGlobals.getSignFont(), pos=(0.0, -0.8), wordwrap=15, mayChange=1)
        self.itText.hide()
        safezoneId = self.getSafezoneId()
        self.sky = loader.loadModel(TagGameGlobals.getSky(safezoneId))
        self.ground = loader.loadModel(TagGameGlobals.getGround(safezoneId))
        self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg')
        self.tagSfx = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg')
        self.itPointer = loader.loadModel('phase_4/models/minigames/bboard-pointer')
        self.tracks = []
        self.initialPositions = TagGameGlobals.getDropPoints(safezoneId)
        self.IT = None

        if TagGameGlobals.isSnowHood(safezoneId):
            self.snow, self.snowRender = SnowUtil.createSnow(self.ground)

        return

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        self.ignoreAll()
        del self.tracks
        del self.IT
        self.sky.removeNode()
        del self.sky
        self.itPointer.removeNode()
        del self.itPointer
        self.ground.removeNode()
        del self.ground
        del self.music
        del self.tagSfx
        self.itText.cleanup()
        del self.itText
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        self.destroySnow()

    def destroySnow(self):
        if hasattr(self, 'snow'):
            self.snow.cleanup()
            self.snowRender.removeNode()
            del self.snow
            del self.snowRender

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.ground.reparentTo(render)
        self.sky.reparentTo(render)
        myPos = self.avIdList.index(self.localAvId)
        base.localAvatar.setPosHpr(*self.initialPositions[myPos])
        base.localAvatar.reparentTo(render)
        base.localAvatar.loop('neutral')
        camera.reparentTo(render)
        camera.setPosHpr(0, -24, 16, 0, -30, 0)
        base.camLens.setFar(450.0)
        base.transitions.irisIn(0.4)
        NametagGlobals.setMasterArrowsOn(1)
        DistributedSmoothNode.activateSmoothing(1, 1)
        self.IT = None

        if hasattr(self, 'snow'):
            self.snow.start(camera, self.snowRender)

        return

    def offstage(self):
        self.notify.debug('offstage')
        DistributedSmoothNode.activateSmoothing(1, 0)
        NametagGlobals.setMasterArrowsOn(0)
        DistributedMinigame.offstage(self)
        self.sky.reparentTo(hidden)
        self.ground.reparentTo(hidden)
        base.camLens.setFar(ToontownGlobals.DefaultCameraFar)
        self.itText.hide()

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        for avId in self.avIdList:
            self.acceptTagEvent(avId)

        myPos = self.avIdList.index(self.localAvId)
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avatar = self.getAvatar(avId)
            if avatar:
                avatar.startSmooth()

        base.localAvatar.setPosHpr(*self.initialPositions[myPos])
        base.localAvatar.d_clearSmoothing()
        base.localAvatar.sendCurrentPosition()
        base.localAvatar.b_setAnimState('neutral', 1)
        base.localAvatar.b_setParent(ToontownGlobals.SPRender)

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

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

    def exitOff(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
            scorePanel.setPos(-0.213, 0.0, 0.28 * i + 0.66)
            scorePanel.reparentTo(base.a2dBottomRight)
            self.scorePanels.append(scorePanel)

        base.setCellsAvailable(base.rightCells, 0)
        self.walkStateData.enter()
        self.walkStateData.fsm.request('walking')
        if base.localAvatar.isIt:
            base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE)
            base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.setTime(self.DURATION)
        self.timer.countdown(self.DURATION, self.timerExpired)
        base.playMusic(self.music, looping=1, volume=0.9)
        base.localAvatar.setIdealCameraPos(Point3(0, -24, 8))

    def exitPlay(self):
        for task in self.tracks:
            task.finish()

        self.tracks = []
        for avId in self.avIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.getGeomNode().clearMat()
                toon.scale = 1.0
                toon.rescaleToon()

        self.walkStateData.exit()
        self.music.stop()
        self.timer.destroy()
        del self.timer
        for panel in self.scorePanels:
            panel.cleanup()

        self.scorePanels = []
        base.setCellsAvailable(base.rightCells, 1)
        base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed)
        base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed)
        self.itPointer.reparentTo(hidden)
        base.localAvatar.cameraIndex = 0
        base.localAvatar.setCameraPositionByIndex(0)

    def timerExpired(self):
        self.notify.debug('local timer expired')
        self.gameOver()

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

    def exitCleanup(self):
        pass

    def setIt(self, avId):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'play':
            self.notify.debug('Ignoring setIt after done playing')
            return
        self.itText.show()
        self.notify.debug(str(avId) + ' is now it')
        if avId == self.localAvId:
            self.itText.setText(TTLocalizer.TagGameYouAreIt)
            base.localAvatar.isIt = 1
            base.localAvatar.controlManager.setSpeeds(OTPGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonJumpForce, OTPGlobals.ToonReverseSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE)
        else:
            self.itText.setText(TTLocalizer.TagGameSomeoneElseIsIt % self.getAvatarName(avId))
            base.localAvatar.isIt = 0
            base.localAvatar.setWalkSpeedNormal()
        avatar = self.getAvatar(avId)
        if avatar:
            self.itPointer.reparentTo(avatar)
            self.itPointer.setZ(avatar.getHeight())
        base.playSfx(self.tagSfx)
        toon = self.getAvatar(avId)
        duration = 0.6
        if not toon:
            return
        spinTrack = LerpHprInterval(toon.getGeomNode(), duration, Point3(0, 0, 0), startHpr=Point3(-5.0 * 360.0, 0, 0), blendType='easeOut')
        growTrack = Parallel()
        gs = 2.5
        for hi in xrange(toon.headParts.getNumPaths()):
            head = toon.headParts[hi]
            growTrack.append(LerpScaleInterval(head, duration, Point3(gs, gs, gs)))

        def bounceFunc(t, trajectory, node = toon.getGeomNode()):
            node.setZ(trajectory.calcZ(t))

        def bounceCleanupFunc(node = toon.getGeomNode(), z = toon.getGeomNode().getZ()):
            node.setZ(z)

        bounceTrack = Sequence()
        startZ = toon.getGeomNode().getZ()
        tLen = 0
        zVel = 30
        decay = 0.6
        while tLen < duration:
            trajectory = Trajectory.Trajectory(0, Point3(0, 0, startZ), Point3(0, 0, zVel), gravMult=5.0)
            dur = trajectory.calcTimeOfImpactOnPlane(startZ)
            if dur <= 0:
                break
            bounceTrack.append(LerpFunctionInterval(bounceFunc, fromData=0.0, toData=dur, duration=dur, extraArgs=[trajectory]))
            tLen += dur
            zVel *= decay

        bounceTrack.append(Func(bounceCleanupFunc))
        tagTrack = Sequence(Func(toon.animFSM.request, 'off'), Parallel(spinTrack, growTrack, bounceTrack), Func(toon.animFSM.request, 'Happy'))
        self.tracks.append(tagTrack)
        tagTrack.start()
        if self.IT:
            it = self.getAvatar(self.IT)
            shrinkTrack = Parallel()
            for hi in xrange(it.headParts.getNumPaths()):
                head = it.headParts[hi]
                scale = ToontownGlobals.toonHeadScales[it.style.getAnimal()]
                shrinkTrack.append(LerpScaleInterval(head, duration, scale))

            self.tracks.append(shrinkTrack)
            shrinkTrack.start()
        self.IT = avId

    def acceptTagEvent(self, avId):
        self.accept('enterdistAvatarCollNode-' + str(avId), self.sendTagIfIt, [avId])

    def sendTagIfIt(self, avId, collisionEntry):
        if base.localAvatar.isIt:
            self.notify.debug('Tagging ' + str(avId))
            self.sendUpdate('tag', [avId])
        else:
            self.notify.debug('Bumped ' + str(avId))

    def setTreasureScore(self, scores):
        if not self.hasLocalToon:
            return
        self.notify.debug('setTreasureScore: %s' % scores)
        for i in xrange(len(self.scorePanels)):
            self.scorePanels[i].setScore(scores[i])
