import math
from direct.showbase.PythonUtil import bound, lerp
from direct.task.Task import Task
from direct.interval.MetaInterval import Sequence, Parallel
from direct.interval.FunctionInterval import Func, Wait
from direct.interval.SoundInterval import SoundInterval
from direct.interval.LerpInterval import LerpScaleInterval, LerpFunc
from direct.directnotify import DirectNotifyGlobal
from pandac.PandaModules import NodePath, Point3, VBase3
from toontown.minigame.OrthoDrive import OrthoDrive
from toontown.minigame.OrthoWalk import OrthoWalk
from toontown.battle.BattleProps import globalPropPool
from toontown.battle.BattleSounds import globalBattleSoundCache
import PartyGlobals
from PartyCogActivityInput import PartyCogActivityInput
from PartyCogActivityGui import PartyCogActivityGui
from PartyCogUtils import CameraManager
from PartyCogUtils import StrafingControl
UPDATE_TASK_NAME = 'PartyCogActivityLocalPlayer_UpdateTask'
THROW_PIE_LIMIT_TIME = 0.2

class PartyCogActivityPlayer:
    toon = None
    position = None
    team = None
    score = 0
    enabled = False
    notify = DirectNotifyGlobal.directNotify.newCategory('PartyCogActivityPlayer')

    def __init__(self, activity, toon, position, team):
        self.activity = activity
        self.position = position
        self.toon = toon
        self.team = team
        self.netTimeSentToStartByHit = 0
        self.kaboomTrack = None
        self.locator = None
        self.teamSpot = self.activity.getIndex(self.toon.doId, self.team)
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splatType = globalPropPool.getPropType(splatName)
        self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg')
        return

    def destroy(self):
        self.cleanUpIvals()
        self.toon = None
        self.locator = None
        self.position = None
        self.pieHitSound = None
        self.splat = None
        return

    def cleanUpIvals(self):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.kaboomTrack = None
        return

    def faceForward(self):
        self.toon.setH(0)

    def setToonStartPosition(self, position):
        self.position = position

    def updateToonPosition(self):
        self.toon.setPos(self.activity.root, self.position)
        self.toon.setH(self.locator, 0.0)

    def setTeam(self, team):
        self.team = team
        self.locator = self.activity.view.teamLocators[self.team]
        self.teamSpot = self.activity.getIndex(self.toon.doId, self.team)

    def entersActivity(self):
        self.locator = self.activity.view.teamLocators[self.team]

    def exitsActivity(self):
        offset = 0
        if self.teamSpot:
            offset = 4.0 * self.teamSpot
        self.toon.wrtReparentTo(render)
        self.toon.setPos(self.locator, offset, -35.0, 0.0)
        self.toon.setH(self.activity.root, 0.0)
        self.locator = None
        return

    def enable(self):
        if self.enabled:
            return
        self.toon.wrtReparentTo(self.locator)
        self.enabled = True

    def disable(self):
        if not self.enabled:
            return
        self.toon.wrtReparentTo(render)
        self.enabled = False
        self.cleanUpIvals()

    def hitBody(self):
        points = PartyGlobals.CogActivityHitPoints
        self.score += points
        return points

    def hitHead(self):
        points = PartyGlobals.CogActivityHitPointsForHead
        self.score += points
        return points

    def resetScore(self):
        self.score = 0

    def respondToPieHit(self, timestamp, pos):
        if self.netTimeSentToStartByHit < timestamp:
            self.__showSplat(pos)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            self.activity.notify.debug('PartyCogPlayer respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit)

    def __showSplat(self, position):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        if not self.pieHitSound:
            self.notify.warning('Trying to play hit sound on destroyed player')
            return
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.toon, position)
        self.splat.setY(self.toon, bound(self.splat.getY(), self.toon.getHeight() / 2.0, position.getY()))
        self.splat.setAlphaScale(1.0)
        targetscale = 0.75

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, node=self.toon, volume=1.0, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return


class PartyCogActivityLocalPlayer(PartyCogActivityPlayer):

    def __init__(self, activity, position, team, exitActivityCallback = None):
        PartyCogActivityPlayer.__init__(self, activity, base.localAvatar, position, team)
        self.input = PartyCogActivityInput(exitActivityCallback)
        self.gui = PartyCogActivityGui()
        self.throwPiePrevTime = 0
        self.lastMoved = 0
        if base.localAvatar:
            self.prevPos = base.localAvatar.getPos()
        self.cameraManager = None
        self.control = None
        self.consecutiveShortThrows = 0
        return

    def destroy(self):
        if self.enabled:
            self.disable()
        if self.cameraManager is not None:
            self.cameraManager.setEnabled(False)
            self.cameraManager.destroy()
        del self.cameraManager
        del self.gui
        del self.input
        if self.control is not None:
            self.control.destroy()
        del self.control
        PartyCogActivityPlayer.destroy(self)
        return

    def _initOrthoWalk(self):
        orthoDrive = OrthoDrive(9.778, customCollisionCallback=self.activity.view.checkOrthoDriveCollision)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def _destroyOrthoWalk(self):
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def getPieThrowingPower(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi
        power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0))
        return power

    def isShortThrow(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        return elapsed <= PartyGlobals.CogActivityShortThrowTime

    def checkForThrowSpam(self, time):
        if self.isShortThrow(time):
            self.consecutiveShortThrows += 1
        else:
            self.consecutiveShortThrows = 0
        return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam

    def _startUpdateTask(self):
        task = Task(self._updateTask)
        task.lastPositionBroadcastTime = 0.0
        self.throwPiePrevTime = 0
        taskMgr.add(task, UPDATE_TASK_NAME)

    def _stopUpdateTask(self):
        taskMgr.remove(UPDATE_TASK_NAME)

    def _updateTask(self, task):
        self._update()
        if base.localAvatar.getPos() != self.prevPos:
            self.prevPos = base.localAvatar.getPos()
            self.lastMoved = self.activity.getCurrentActivityTime()
        if max(self.activity.getCurrentActivityTime() - self.lastMoved, 0) > PartyGlobals.ToonMoveIdleThreshold:
            self.gui.showMoveControls()
        if max(self.activity.getCurrentActivityTime() - self.throwPiePrevTime, 0) > PartyGlobals.ToonAttackIdleThreshold:
            self.gui.showAttackControls()
        if self.input.throwPieWasReleased:
            if self.checkForThrowSpam(globalClock.getFrameTime()):
                self.gui.showSpamWarning()
            self.input.throwPieWasReleased = False
            self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime()))
        return Task.cont

    def throwPie(self, piePower):
        if not self.activity.isState('Active'):
            return
        if self.activity.getCurrentActivityTime() - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME:
            self.throwPiePrevTime = self.activity.getCurrentActivityTime()
            self.activity.b_pieThrow(self.toon, piePower)

    def _update(self):
        self.control.update()

    def getLookat(self, whosLooking, refNode = None):
        if refNode is None:
            refNode = render
        dist = 5.0
        oldParent = self.tempNP.getParent()
        self.tempNP.reparentTo(whosLooking)
        self.tempNP.setPos(0.0, dist, 0.0)
        pos = self.tempNP.getPos(refNode)
        self.tempNP.reparentTo(oldParent)
        return pos

    def entersActivity(self):
        base.cr.playGame.getPlace().setState('activity')
        PartyCogActivityPlayer.entersActivity(self)
        self.gui.disableToontownHUD()
        self.cameraManager = CameraManager(camera)
        self.tempNP = NodePath('temp')
        self.lookAtMyTeam()
        self.control = StrafingControl(self)

    def exitsActivity(self):
        PartyCogActivityPlayer.exitsActivity(self)
        self.gui.enableToontownHUD()
        self.cameraManager.setEnabled(False)
        self.tempNP.removeNode()
        self.tempNP = None
        if not aspect2d.find('**/JellybeanRewardGui*'):
            base.cr.playGame.getPlace().setState('walk')
        else:
            self.toon.startPosHprBroadcast()
        return

    def getRunToStartPositionIval(self):
        targetH = self.locator.getH()
        travelVec = self.position - self.toon.getPos(self.activity.root)
        duration = travelVec.length() / 9.778
        startH = 0.0
        if travelVec.getY() < 0.0:
            startH = 180.0
        return Sequence(Func(self.toon.startPosHprBroadcast, 0.1), Func(self.toon.b_setAnimState, 'run'), Parallel(self.toon.hprInterval(0.5, VBase3(startH, 0.0, 0.0), other=self.activity.root), self.toon.posInterval(duration, self.position, other=self.activity.root)), Func(self.toon.b_setAnimState, 'neutral'), self.toon.hprInterval(0.25, VBase3(targetH, 0.0, 0.0), other=self.activity.root), Func(self.toon.stopPosHprBroadcast))

    def enable(self):
        if self.enabled:
            return
        PartyCogActivityPlayer.enable(self)
        self.toon.b_setAnimState('Happy')
        self._initOrthoWalk()
        self.orthoWalk.start()
        self.orthoWalking = True
        self.input.enable()
        self.gui.disableToontownHUD()
        self.gui.load()
        self.gui.setScore(0)
        self.gui.showScore()
        self.gui.setTeam(self.team)
        self.gui.startTrackingCogs(self.activity.view.cogManager.cogs)
        self.control.enable()
        self._startUpdateTask()

    def disable(self):
        if not self.enabled:
            return
        self._stopUpdateTask()
        self.toon.b_setAnimState('neutral')
        PartyCogActivityPlayer.disable(self)
        self.orthoWalking = False
        self.orthoWalk.stop()
        self._destroyOrthoWalk()
        self.input.disable()
        self._aimMode = False
        self.cameraManager.setEnabled(False)
        self.gui.hide()
        self.gui.stopTrackingCogs()
        self.gui.unload()

    def updateScore(self):
        self.gui.setScore(self.score)

    def b_updateToonPosition(self):
        self.updateToonPosition()
        self.d_updateToonPosition()

    def d_updateToonPosition(self):
        self.toon.d_setPos(self.toon.getX(), self.toon.getY(), self.toon.getZ())
        self.toon.d_setH(self.toon.getH())

    def lookAtArena(self):
        self.cameraManager.setEnabled(True)
        self.cameraManager.setTargetPos(self.activity.view.arena.find('**/conclusionCamPos_locator').getPos(render))
        self.cameraManager.setTargetLookAtPos(self.activity.view.arena.find('**/conclusionCamAim_locator').getPos(render))

    def lookAtMyTeam(self):
        activityView = self.activity.view
        arena = activityView.arena
        pos = activityView.teamCamPosLocators[self.team].getPos()
        aim = activityView.teamCamAimLocators[self.team].getPos()
        camera.wrtReparentTo(arena)
        self.cameraManager.setPos(camera.getPos(render))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, pos)
        self.cameraManager.setTargetPos(self.tempNP.getPos(render))
        self.cameraManager.setLookAtPos(self.getLookat(camera))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, aim)
        self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render))
        self.cameraManager.setEnabled(True)
        camera.setP(0.0)
        camera.setR(0.0)
