from otp.ai.AIBaseGlobal import *
from GoonGlobals import *
from direct.directnotify import DirectNotifyGlobal
from toontown.battle import SuitBattleGlobals
from toontown.coghq import DistributedCrushableEntityAI
import GoonPathData
from direct.distributed import ClockDelta
import random
from direct.task import Task

class DistributedGoonAI(DistributedCrushableEntityAI.DistributedCrushableEntityAI):
    UPDATE_TIMESTAMP_INTERVAL = 180.0
    STUN_TIME = 4
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoonAI')

    def __init__(self, level, entId):
        self.hFov = 70
        self.attackRadius = 15
        self.strength = 15
        self.velocity = 4
        self.scale = 1.0
        DistributedCrushableEntityAI.DistributedCrushableEntityAI.__init__(self, level, entId)
        self.curInd = 0
        self.dir = GOON_FORWARD
        self.parameterized = 0
        self.width = 1
        self.crushed = 0
        self.pathStartTime = None
        self.walkTrackTime = 0.0
        self.totalPathTime = 1.0
        return

    def delete(self):
        taskMgr.remove(self.taskName('sync'))
        taskMgr.remove(self.taskName('resumeWalk'))
        taskMgr.remove(self.taskName('recovery'))
        taskMgr.remove(self.taskName('deleteGoon'))
        taskMgr.remove(self.taskName('GoonBombCheck'))
        DistributedCrushableEntityAI.DistributedCrushableEntityAI.delete(self)

    def generate(self):
        self.notify.debug('generate')
        DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self)
        if self.level:
            self.level.setEntityCreateCallback(self.parentEntId, self.startGoon)

    def startGoon(self):
        ts = 100 * random.random()
        self.sendMovie(GOON_MOVIE_WALK, pauseTime=ts)

    def requestBattle(self, pauseTime):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('requestBattle, avId = %s' % avId)
        self.sendMovie(GOON_MOVIE_BATTLE, avId, pauseTime)
        taskMgr.remove(self.taskName('resumeWalk'))
        taskMgr.doMethodLater(5, self.sendMovie, self.taskName('resumeWalk'), extraArgs=(GOON_MOVIE_WALK, avId, pauseTime))

    def requestStunned(self, pauseTime):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('requestStunned(%s)' % avId)
        if self.level:
            if not hasattr(self.level, 'goonStunRequests'):
                self.level.goonStunRequests = {}
            if taskMgr.hasTaskNamed(self.taskName('GoonBombCheck')):
                if self.level.goonStunRequests.has_key(avId):
                    self.level.goonStunRequests[avId] = self.level.goonStunRequests[avId] + 1
                else:
                    self.level.goonStunRequests[avId] = 1
            else:
                self.level.goonStunRequests[avId] = 1
                taskMgr.doMethodLater(0.1, self.accumulateGoonMessages, self.taskName('GoonBombCheck'))
        self.sendMovie(GOON_MOVIE_STUNNED, avId, pauseTime)
        taskMgr.remove(self.taskName('recovery'))
        taskMgr.doMethodLater(self.STUN_TIME, self.sendMovie, self.taskName('recovery'), extraArgs=(GOON_MOVIE_RECOVERY, avId, pauseTime))

    def accumulateGoonMessages(self, task):
        if not hasattr(self.level, 'goonStunRequests'):
            return
        for toonId in self.level.goonStunRequests:
            if self.level.goonStunRequests[toonId] > 2:
                self.air.writeServerEvent('suspicious', toonId, 'Stunned multiple goons very close together. Possible multihack.')

        self.level.goonStunRequests.clear()
        del self.level.goonStunRequests

    def requestResync(self, task=None):
        self.notify.debug('resyncGoon')
        self.sendMovie(GOON_MOVIE_SYNC)
        self.updateGrid()

    def sendMovie(self, type, avId=0, pauseTime=0):
        if type == GOON_MOVIE_WALK:
            self.pathStartTime = globalClock.getFrameTime()
            if self.parameterized:
                self.walkTrackTime = pauseTime % self.totalPathTime
            else:
                self.walkTrackTime = pauseTime
            self.notify.debug('GOON_MOVIE_WALK doId = %s, pathStartTime = %s, walkTrackTime = %s' % (self.doId, self.pathStartTime, self.walkTrackTime))
        if type == GOON_MOVIE_WALK or type == GOON_MOVIE_SYNC:
            curT = globalClock.getFrameTime()
            elapsedT = curT - self.pathStartTime
            pathT = self.walkTrackTime + elapsedT
            if self.parameterized:
                pathT = pathT % self.totalPathTime
            self.sendUpdate('setMovie', [type, avId, pathT, ClockDelta.globalClockDelta.localToNetworkTime(curT)])
            taskMgr.remove(self.taskName('sync'))
            taskMgr.doMethodLater(self.UPDATE_TIMESTAMP_INTERVAL, self.requestResync, self.taskName('sync'), extraArgs=None)
        else:
            self.sendUpdate('setMovie', [type, avId, pauseTime, ClockDelta.globalClockDelta.getFrameNetworkTime()])
        return

    def updateGrid(self):
        if not self.parameterized:
            return
        if self.grid and hasattr(self, 'entId'):
            self.grid.removeObject(self.entId)
            if not self.crushed:
                curT = globalClock.getFrameTime()
                if self.pathStartTime:
                    elapsedT = curT - self.pathStartTime
                else:
                    elapsedT = 0
                pathT = (self.walkTrackTime + elapsedT) % self.totalPathTime
                pt = self.getPathPoint(pathT)
                if not self.grid.addObjectByPos(self.entId, pt):
                    self.notify.warning("updateGrid: couldn't put goon in grid")

    def doCrush(self, crusherId, axis):
        self.notify.debug('doCrush %s' % self.doId)
        DistributedCrushableEntityAI.DistributedCrushableEntityAI.doCrush(self, crusherId, axis)
        self.crushed = 1
        self.grid.removeObject(self.entId)
        taskMgr.doMethodLater(5.0, self.doDelete, self.taskName('deleteGoon'))

    def doDelete(self, task):
        self.requestDelete()
        return Task.done

    def setParameterize(self, x, y, z, pathIndex):
        if not hasattr(self, 'level') or not self.level:
            return
        pathId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()]
        pathData = GoonPathData.Paths[pathId]
        self.pathOrigin = Vec3(x, y, z)
        if pathIndex > len(pathData):
            self.notify.warning('Invalid path index given, using 0')
            pathIndex = 0
        pathPts = pathData[pathIndex] + [pathData[pathIndex][0]]
        invVel = 1.0 / self.velocity
        t = 0
        self.tSeg = [t]
        self.pathSeg = []
        for i in range(len(pathPts) - 1):
            ptA = pathPts[i]
            ptB = pathPts[i + 1]
            t += T_TURN
            self.tSeg.append(t)
            self.pathSeg.append([Vec3(0, 0, 0), 0, ptA])
            seg = Vec3(ptB - ptA)
            segLength = seg.length()
            t += invVel * segLength
            self.tSeg.append(t)
            self.pathSeg.append([seg, segLength, ptA])

        self.totalPathTime = t
        self.pathPts = pathPts
        self.parameterized = 1

    def getPathPoint(self, t):
        for i in range(len(self.tSeg) - 1):
            if t >= self.tSeg[i] and t < self.tSeg[i + 1]:
                tSeg = t - self.tSeg[i]
                t = tSeg / (self.tSeg[i + 1] - self.tSeg[i])
                seg = self.pathSeg[i][0]
                ptA = self.pathSeg[i][2]
                pt = ptA + seg * t
                return self.pathOrigin + pt

        self.notify.warning("Couldn't find valid path point")
        return Vec3(0, 0, 0)

    def b_setVelocity(self, velocity):
        self.setVelocity(velocity)
        self.d_setVelocity(velocity)

    def setVelocity(self, velocity):
        self.velocity = velocity

    def d_setVelocity(self, velocity):
        self.sendUpdate('setVelocity', [velocity])

    def getVelocity(self):
        return self.velocity

    def b_setHFov(self, hFov):
        self.setHFov(hFov)
        self.d_setHFov(hFov)

    def setHFov(self, hFov):
        self.hFov = hFov

    def d_setHFov(self, hFov):
        self.sendUpdate('setHFov', [hFov])

    def getHFov(self):
        return self.hFov

    def b_setAttackRadius(self, attackRadius):
        self.setAttackRadius(attackRadius)
        self.d_setAttackRadius(attackRadius)

    def setAttackRadius(self, attackRadius):
        self.attackRadius = attackRadius

    def d_setAttackRadius(self, attackRadius):
        self.sendUpdate('setAttackRadius', [attackRadius])

    def getAttackRadius(self):
        return self.attackRadius

    def b_setStrength(self, strength):
        self.setStrength(strength)
        self.d_setStrength(strength)

    def setStrength(self, strength):
        self.strength = strength

    def d_setStrength(self, strength):
        self.sendUpdate('setStrength', [strength])

    def getStrength(self):
        return self.strength

    def b_setGoonScale(self, scale):
        self.setGoonScale(scale)
        self.d_setGoonScale(scale)

    def setGoonScale(self, scale):
        self.scale = scale

    def d_setGoonScale(self, scale):
        self.sendUpdate('setGoonScale', [scale])

    def getGoonScale(self):
        return self.scale

    def b_setupGoon(self, velocity, hFov, attackRadius, strength, scale):
        self.setupGoon(velocity, hFov, attackRadius, strength, scale)
        self.d_setupGoon(velocity, hFov, attackRadius, strength, scale)

    def setupGoon(self, velocity, hFov, attackRadius, strength, scale):
        self.setVelocity(velocity)
        self.setHFov(hFov)
        self.setAttackRadius(attackRadius)
        self.setStrength(strength)
        self.setGoonScale(scale)

    def d_setupGoon(self, velocity, hFov, attackRadius, strength, scale):
        self.sendUpdate('setupGoon', [velocity, hFov, attackRadius, strength, scale])