from panda3d.core import *
from direct.interval.IntervalGlobal import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from direct.directnotify import DirectNotifyGlobal
import DistributedSuitBase
from toontown.toonbase import ToontownGlobals
from toontown.battle import MovieUtil

class DistributedLawbotBossSuit(DistributedSuitBase.DistributedSuitBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossSuit')
    timeToShow = 1.0
    timeToRelease = 3.15
    throwPaperEndTime = 4.33

    def __init__(self, cr):
        self.flyingEvidenceTrack = None
        try:
            self.DistributedSuit_initialized
        except:
            self.DistributedSuit_initialized = 1
            DistributedSuitBase.DistributedSuitBase.__init__(self, cr)
            self.activeIntervals = {}
            self.boss = None
            self.fsm = ClassicFSM.ClassicFSM('DistributedLawbotBossSuit', [
                State.State('Off',
                            self.enterOff,
                            self.exitOff, [
                                'Walk',
                                'Battle',
                                'neutral']),
                State.State('Walk',
                            self.enterWalk,
                            self.exitWalk, [
                                'WaitForBattle',
                                'Battle']),
                State.State('Battle',
                            self.enterBattle,
                            self.exitBattle, []),
                State.State('neutral',
                            self.enterNeutral,
                            self.exitNeutral, [
                                'PreThrowProsecute',
                                'PreThrowAttack',
                                'Stunned']),
                State.State('PreThrowProsecute',
                            self.enterPreThrowProsecute,
                            self.exitPreThrowProsecute,
                            ['PostThrowProsecute',
                             'neutral',
                             'Stunned']),
                State.State('PostThrowProsecute',
                            self.enterPostThrowProsecute,
                            self.exitPostThrowProsecute, [
                                'neutral',
                                'Stunned']),
                State.State('PreThrowAttack',
                            self.enterPreThrowAttack,
                            self.exitPreThrowAttack, [
                                'PostThrowAttack',
                                'neutral',
                                'Stunned']),
                State.State('PostThrowAttack',
                            self.enterPostThrowAttack,
                            self.exitPostThrowAttack, [
                                'neutral',
                                'Stunned']),
                State.State('Stunned',
                            self.enterStunned,
                            self.exitStunned, [
                                'neutral']),
                State.State('WaitForBattle',
                            self.enterWaitForBattle,
                            self.exitWaitForBattle, [
                                'Battle'])],
                'Off', 'Off')
            self.fsm.enterInitialState()

        return

    def generate(self):
        self.notify.debug('DLBS.generate:')
        DistributedSuitBase.DistributedSuitBase.generate(self)

    def announceGenerate(self):
        DistributedSuitBase.DistributedSuitBase.announceGenerate(self)
        self.notify.debug('DLBS.announceGenerate')
        colNode = self.find('**/distAvatarCollNode*')
        colNode.setTag('pieCode', str(ToontownGlobals.PieCodeLawyer))
        self.attackEvidenceA = self.getEvidence(True)
        self.attackEvidenceB = self.getEvidence(True)
        self.attackEvidence = self.attackEvidenceA
        self.prosecuteEvidence = self.getEvidence(False)
        self.hideName()
        self.setPickable(False)

    def disable(self):
        self.notify.debug('DistributedSuit %d: disabling' % self.getDoId())
        self.setState('Off')
        DistributedSuitBase.DistributedSuitBase.disable(self)
        self.cleanupIntervals()
        self.boss = None
        return

    def delete(self):
        try:
            self.DistributedSuit_deleted
        except:
            self.DistributedSuit_deleted = 1
            self.notify.debug('DistributedSuit %d: deleting' % self.getDoId())
            del self.fsm
            DistributedSuitBase.DistributedSuitBase.delete(self)

    def d_requestBattle(self, pos, hpr):
        self.cr.playGame.getPlace().setState('WaitForBattle')
        self.sendUpdate('requestBattle', [pos[0],
         pos[1],
         pos[2],
         hpr[0],
         hpr[1],
         hpr[2]])
        return None

    def __handleToonCollision(self, collEntry):
        toonId = base.localAvatar.getDoId()
        self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId)
        self.d_requestBattle(self.getPos(), self.getHpr())
        self.setState('WaitForBattle')
        return None

    def enterWalk(self):
        self.notify.debug('enterWalk')
        self.enableBattleDetect('walk', self.__handleToonCollision)
        self.loop('walk', 0)
        pathPoints = [Vec3(50, 15, 0),
         Vec3(50, 25, 0),
         Vec3(20, 25, 0),
         Vec3(20, 15, 0),
         Vec3(50, 15, 0)]
        self.tutWalkTrack = self.makePathTrack(self, pathPoints, 4.5, 'tutFlunkyWalk')
        self.tutWalkTrack.loop()

    def exitWalk(self):
        self.notify.debug('exitWalk')
        self.disableBattleDetect()
        self.tutWalkTrack.pause()
        self.tutWalkTrack = None
        return

    def enterNeutral(self):
        self.notify.debug('enterNeutral')
        self.notify.debug('DistributedLawbotBossSuit: Neutral')
        self.loop('neutral', 0)

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

    def doAttack(self, x1, y1, z1, x2, y2, z2):
        self.notify.debug('x1=%.2f y1=%.2f z2=%.2f x2=%.2f y2=%.2f z2=%.2f' % (x1,
         y1,
         z1,
         x2,
         y2,
         z2))
        self.curTargetPt = Point3(x2, y2, z2)
        self.fsm.request('PreThrowAttack')
        return
        attackEvidence = self.getEvidence(True)
        nodePath = render
        node = nodePath.attachNewNode('attackEvidence-%s' % self.doId)
        node.setPos(x1, y1, z1)
        duration = 3.0
        throwName = self.uniqueName('lawyerAttack')
        throwingSeq = self.makeAttackThrowingTrack(attackEvidence, duration, Point3(x2, y2, z2))
        fullSequence = Sequence(throwingSeq, name=throwName)
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()

    def doProsecute(self):
        self.notify.debug('doProsecute')
        bounds = self.boss.prosecutionColNodePath.getBounds()
        panCenter = bounds.getCenter()
        localPos = panCenter
        prosecutionPanPos = render.getRelativePoint(self.boss.prosecutionColNodePath, localPos)
        self.curTargetPt = prosecutionPanPos
        self.fsm.request('PreThrowProsecute')
        return
        attackEvidence = self.getEvidence(False)
        nodePath = render
        node = nodePath.attachNewNode('prosecuteEvidence-%s' % self.doId)
        node.setPos(self.getPos())
        duration = ToontownGlobals.LawbotBossLawyerToPanTime
        throwName = self.uniqueName('lawyerProsecute')
        throwingSeq = self.makeProsecuteThrowingTrack(attackEvidence, duration, prosecutionPanPos)
        fullSequence = Sequence(throwingSeq, Func(self.boss.flashGreen), Func(self.clearInterval, throwName), name=throwName)
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()

    def makeDummySequence(self):
        retval = Sequence(Wait(10))
        return retval

    def makeProsecuteThrowingTrack(self, evidence, inFlightDuration, hitPos):
        suitTrack = Sequence()
        suitTrack.append(ActorInterval(self, 'throw-paper'))
        throwPaperDuration = suitTrack.getDuration()
        inFlight = Parallel(evidence.posInterval(inFlightDuration, hitPos, fluid=1))
        origHpr = self.getHpr()
        self.headsUp(hitPos)
        newHpr = self.getHpr()
        self.setHpr(origHpr)
        rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1))
        propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(self.makeDummySequence), inFlight, Func(evidence.detachNode))
        throwingTrack = Parallel(suitTrack, propTrack, rotateTrack)
        return throwingTrack

    def makeAttackThrowingTrack(self, evidence, inFlightDuration, hitPos):
        suitTrack = Sequence()
        suitTrack.append(ActorInterval(self, 'throw-paper'))
        throwPaperDuration = suitTrack.getDuration()
        origHpr = self.getHpr()
        self.headsUp(hitPos)
        newHpr = self.getHpr()
        self.setHpr(origHpr)
        rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1))
        propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.detachNode))
        throwingTrack = Parallel(suitTrack, propTrack, rotateTrack)
        return throwingTrack

    def makePreThrowAttackTrack(self, evidence, inFlightDuration, hitPos):
        suitTrack = Sequence()
        suitTrack.append(ActorInterval(self, 'throw-paper', endTime=self.timeToRelease))
        throwPaperDuration = suitTrack.getDuration()
        origHpr = self.getHpr()
        self.headsUp(hitPos)
        newHpr = self.getHpr()
        self.setHpr(origHpr)
        rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1))
        propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.setScale, 1), Func(evidence.setHpr, 0, 0, 0), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow))
        throwingTrack = Parallel(suitTrack, propTrack, rotateTrack)
        return throwingTrack

    def makePostThrowAttackTrack(self, evidence, inFlightDuration, hitPos):
        suitTrack = Sequence()
        suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease))
        propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide))
        return (suitTrack, propTrack)

    def makePreThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos):
        return self.makePreThrowAttackTrack(evidence, inFlightDuration, hitPos)

    def makePostThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos):
        suitTrack = Sequence()
        suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease))
        propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide))
        return (suitTrack, propTrack)

    def getEvidence(self, usedForAttack = False):
        model = loader.loadModel('phase_5/models/props/lawbook')
        if usedForAttack:
            bounds = model.getBounds()
            center = bounds.getCenter()
            radius = bounds.getRadius()
            sphere = CollisionSphere(center.getX(), center.getY(), center.getZ(), radius)
            colNode = CollisionNode('BossZap')
            colNode.setTag('attackCode', str(ToontownGlobals.BossCogLawyerAttack))
            colNode.addSolid(sphere)
            model.attachNewNode(colNode)
            model.setTransparency(1)
            model.setAlphaScale(0.5)
        return model

    def cleanupIntervals(self):
        for interval in self.activeIntervals.values():
            interval.finish()

        self.activeIntervals = {}

    def clearInterval(self, name, finish = 1):
        if name in self.activeIntervals:
            ival = self.activeIntervals[name]
            if finish:
                ival.finish()
            else:
                ival.pause()
            if name in self.activeIntervals:
                del self.activeIntervals[name]
        else:
            self.notify.debug('interval: %s already cleared' % name)

    def setBossCogId(self, bossCogId):
        self.bossCogId = bossCogId
        self.boss = base.cr.doId2do[bossCogId]

    def doStun(self):
        self.notify.debug('doStun')
        self.fsm.request('Stunned')

    def enterPreThrowProsecute(self):
        duration = ToontownGlobals.LawbotBossLawyerToPanTime
        throwName = self.uniqueName('preThrowProsecute')
        preThrowTrack = self.makePreThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt)
        fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowProsecute'), name=throwName)
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()

    def exitPreThrowProsecute(self):
        throwName = self.uniqueName('preThrowProsecute')
        if throwName in self.activeIntervals:
            self.activeIntervals[throwName].pause()
            del self.activeIntervals[throwName]

    def enterPostThrowProsecute(self):
        duration = ToontownGlobals.LawbotBossLawyerToPanTime
        throwName = self.uniqueName('postThrowProsecute')
        postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt)
        fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName)
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()
        flyName = self.uniqueName('flyingEvidence')
        self.activeIntervals[flyName] = self.flyingEvidenceTrack
        self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'prosecute'))
        self.flyingEvidenceTrack.start()

    def exitPostThrowProsecute(self):
        throwName = self.uniqueName('postThrowProsecute')
        if throwName in self.activeIntervals:
            self.activeIntervals[throwName].finish()
            del self.activeIntervals[throwName]

    def requestStateIfNotInFlux(self, state):
        if not self.fsm._ClassicFSM__internalStateInFlux:
            self.fsm.request(state)

    def enterPreThrowAttack(self):
        if self.attackEvidence == self.attackEvidenceA:
            self.attackEvidence = self.attackEvidenceB
        else:
            self.attackEvidence = self.attackEvidenceA
        duration = 3.0
        throwName = self.uniqueName('preThrowAttack')
        preThrowTrack = self.makePreThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt)
        fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowAttack'), name=throwName)
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()

    def exitPreThrowAttack(self):
        throwName = self.uniqueName('preThrowAttack')
        if throwName in self.activeIntervals:
            self.activeIntervals[throwName].pause()
            del self.activeIntervals[throwName]

    def enterPostThrowAttack(self):
        duration = 3.0
        throwName = self.uniqueName('postThrowAttack')
        postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt)
        fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName)
        self.notify.debug('duration of postThrowAttack = %f' % fullSequence.getDuration())
        self.activeIntervals[throwName] = fullSequence
        fullSequence.start()
        flyName = self.uniqueName('flyingEvidence')
        self.activeIntervals[flyName] = self.flyingEvidenceTrack
        self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'attack'))
        self.flyingEvidenceTrack.start()

    def finishedWithFlying(self, str):
        self.notify.debug('finished flyingEvidenceTrack %s' % str)

    def exitPostThrowAttack(self):
        throwName = self.uniqueName('postThrowAttack')
        if throwName in self.activeIntervals:
            self.activeIntervals[throwName].finish()
            del self.activeIntervals[throwName]

    def enterStunned(self):
        stunSequence = MovieUtil.createSuitStunInterval(self, 0, ToontownGlobals.LawbotBossLawyerStunTime)
        seqName = stunSequence.getName()
        stunSequence.append(Func(self.fsm.request, 'neutral'))
        self.activeIntervals[seqName] = stunSequence
        stunSequence.start()

    def exitStunned(self):
        self.prosecuteEvidence.hide()
        self.attackEvidence.hide()
