from panda3d.core import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase.DirectObject import DirectObject
from direct.showbase import PythonUtil
from direct.interval.IntervalGlobal import *
from toontown.minigame import ToonBlitzGlobals
from toontown.toonbase import ToontownGlobals
from toontown.suit import Suit
from toontown.suit import SuitDNA
from toontown.battle.BattleProps import *
from toontown.battle import MovieUtil
from toontown.battle import BattleParticles, BattleProps
from direct.particles import ParticleEffect
import math, random
COLOR_RED = VBase4(1, 0, 0, 0.3)

class TwoDEnemy(DirectObject):
    notify = DirectNotifyGlobal.directNotify.newCategory('TwoDEnemy')

    def __init__(self, enemyMgr, index, suitAttribs):
        self.enemyMgr = enemyMgr
        self.game = self.enemyMgr.section.sectionMgr.game
        self.index = index
        self.moveIval = None
        self.propTrack = None
        self.animTrack = None
        self.shotTrack = None
        self.deathTrack = None
        self.deathSuit = None
        self.suitSound = None
        self.deleteMeCallback = None
        self.isMovingUpDown = False
        self.isMovingLeftRight = False
        self.showCollSpheres = False
        self.isDestroyed = False
        self.isGoingUp = False
        self.setupEnemy(suitAttribs)
        BattleParticles.loadParticles()
        return

    def destroy(self):
        if self.isDestroyed:
            return
        self.isDestroyed = True
        if hasattr(self.suit, 'prop') and self.suit.prop:
            self.suit.prop.stash()
        if self.propTrack:
            self.propTrack.finish()
            self.propTrack = None
        if self.suitSound:
            self.suitSound.stop()
            del self.suitSound
        if self.animTrack:
            self.animTrack.finish()
            self.animTrack = None
        if self.shotTrack != None:
            self.shotTrack.finish()
            self.shotTrack = None
        if self.deathTrack != None:
            self.deathTrack.finish()
            self.deathTrack = None
        if self.deathSuit:
            self.deathSuit.detachNode()
            self.suit.cleanupLoseActor()
            self.deathSuit = None
        if self.moveIval:
            self.moveIval.pause()
            del self.moveIval
        if self.suit:
            self.suit.delete()
            self.suit = None
        BattleParticles.unloadParticles()
        self.ignore(self.game.uniqueName('enter' + self.suitName))
        self.game = None
        self.enemyMgr = None
        return

    def setupEnemy(self, suitAttribs):
        suitType = suitAttribs[0]
        self.suit = Suit.Suit()
        suitDNA = SuitDNA.SuitDNA()
        suitDNA.newSuit(suitType)
        self.suit.setDNA(suitDNA)
        self.suit.pose('walk', 0)
        self.suitName = 'Enemy-%s' % self.index
        self.suit.setName(self.suitName)
        self.suit.nametag3d.stash()
        self.suit.nametag.destroy()
        suitPosAttribs = suitAttribs[1]
        initX, initY, initZ = suitPosAttribs[0]
        initPos = Point3(initX, initY, initZ)
        if len(suitPosAttribs) == 3:
            finalX, finalY, finalZ = suitPosAttribs[1]
            finalPos = Point3(finalX, finalY, finalZ)
            posIvalDuration = suitPosAttribs[2]
            self.clearMoveIval()

            def getForwardIval(blendTypeStr, self = self):
                forwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=finalPos, startPos=initPos, name='%s-moveFront' % self.suitName, blendType=blendTypeStr, fluid=1)
                return forwardIval

            def getBackwardIval(blendTypeStr, self = self):
                backwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=initPos, startPos=finalPos, name='%s-moveBack' % self.suitName, blendType=blendTypeStr, fluid=1)
                return backwardIval

            if abs(finalZ - initZ) > 0.0:

                def setIsGoingUp(value):
                    self.isGoingUp = value

                self.isMovingUpDown = True
                self.suit.setH(90)
                self.suit.prop = None
                if self.suit.prop == None:
                    self.suit.prop = BattleProps.globalPropPool.getProp('propeller')
                    self.suit.prop.setScale(1.1)
                    self.suit.prop.setColor(1, 1, 0.6, 1)
                head = self.suit.find('**/joint_head')
                self.suit.prop.reparentTo(head)
                self.propTrack = Sequence(ActorInterval(self.suit.prop, 'propeller', startFrame=8, endFrame=25, playRate=2.0))
                self.animTrack = Sequence(ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=-0.5))
                self.moveIval = Sequence(Func(setIsGoingUp, True), getForwardIval('easeInOut'), Func(setIsGoingUp, False), getBackwardIval('easeInOut'))
                self.suitSound = base.loadSfx('phase_4/audio/sfx/TB_propeller.ogg')
            else:
                self.isMovingLeftRight = True
                self.moveIval = Sequence(Func(self.setHeading, finalPos, initPos), getForwardIval('noBlend'), Func(self.setHeading, initPos, finalPos), getBackwardIval('noBlend'))
        self.suit.setPos(initX, initY, initZ)
        self.suit.dropShadow.hide()
        self.setupCollision()
        return

    def setupCollision(self):
        collSphere = CollisionSphere(0, 0, 2, 2)
        collSphere.setTangible(1)
        collNode = CollisionNode(self.game.uniqueName(self.suitName))
        collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        collNode.addSolid(collSphere)
        self.collNodePath = self.suit.attachNewNode(collNode)
        self.collNodePath.hide()
        if self.showCollSpheres:
            self.collNodePath.show()
        self.accept(self.game.uniqueName('enter' + self.suitName), self.handleEnemyCollision)

    def clearMoveIval(self):
        if self.moveIval:
            self.moveIval.pause()
            del self.moveIval
        self.moveIval = None
        return

    def start(self, elapsedTime):
        if self.moveIval:
            self.moveIval.loop()
            self.moveIval.setT(elapsedTime)
        if self.isMovingLeftRight:
            self.suit.loop('walk')
        elif self.isMovingUpDown:
            self.propTrack.loop()
            self.animTrack.loop()
            base.playSfx(self.suitSound, node=self.suit, looping=1)

    def enterPause(self):
        if hasattr(self, 'moveIval') and self.moveIval:
            self.moveIval.pause()
            self.suit.loop('neutral')
        if self.suitSound:
            self.suitSound.stop()

    def exitPause(self):
        if hasattr(self, 'moveIval') and self.moveIval:
            self.moveIval.resume()
            if self.isMovingLeftRight:
                self.suit.loop('walk')
            elif self.isMovingUpDown:
                self.propTrack.loop()
                self.animTrack.loop()
                base.playSfx(self.suitSound, node=self.suit, looping=1, volume=0.1)

    def handleEnemyCollision(self, cevent):
        messenger.send('enemyHit')

    def setHeading(self, finalPos, initPos):
        diffX = finalPos.getX() - initPos.getX()
        angle = -90 * diffX / math.fabs(diffX)
        startAngle = self.suit.getH()
        startAngle = PythonUtil.fitSrcAngle2Dest(startAngle, angle)
        dur = 0.1 * abs(startAngle - angle) / 90
        self.suitTurnIval = LerpHprInterval(self.suit, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='SuitLerpHpr')
        self.suitTurnIval.start()

    def blinkColor(self, color, duration):
        blink = Sequence(LerpColorScaleInterval(self.suit, 0.5, color, startColorScale=VBase4(1, 1, 1, 1)), LerpColorScaleInterval(self.suit, 0.5, VBase4(1, 1, 1, 1), startColorScale=color))
        track = Sequence(Func(blink.loop), Wait(duration), Func(blink.finish))
        return track

    def doShotTrack(self):
        blinkRed = self.blinkColor(COLOR_RED, 2)
        point = Point3(self.suit.getX(render), self.suit.getY(render), self.suit.getZ(render) + self.suit.height / 2.0)
        scale = 0.3
        splashHold = 0.1

        def prepSplash(splash, point):
            if callable(point):
                point = point()
            splash.reparentTo(render)
            splash.setPos(point)
            scale = splash.getScale()
            splash.setBillboardPointWorld()
            splash.setScale(scale)

        splash = globalPropPool.getProp('splash-from-splat')
        splash.setScale(scale)
        splashTrack = Sequence(Func(prepSplash, splash, point), ActorInterval(splash, 'splash-from-splat'), Wait(splashHold), Func(MovieUtil.removeProp, splash))
        self.shotTrack = Parallel(Func(self.game.assetMgr.playSplashSound), blinkRed, splashTrack)
        self.shotTrack.start()

    def doDeathTrack(self):

        def removeDeathSuit(suit, deathSuit):
            if not deathSuit.isEmpty():
                deathSuit.detachNode()
                suit.cleanupLoseActor()

        if self.suitSound:
            self.suitSound.stop()
        self.deathSuit = self.suit.getLoseActor()
        self.deathSuit.reparentTo(self.enemyMgr.enemiesNP)
        self.deathSuit.setPos(render, self.suit.getPos(render))
        self.deathSuit.setHpr(render, self.suit.getHpr(render))
        self.suit.hide()
        self.collNodePath.reparentTo(self.deathSuit)
        treasureSpawnPoint = Point3(self.suit.getX(), self.suit.getY(), self.suit.getZ() + self.suit.height / 2.0)
        gearPoint = Point3(0, 0, self.suit.height / 2.0 + 2.0)
        spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg')
        deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6))
        smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall')
        singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1)
        smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10)
        bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30)
        smallGears.setPos(gearPoint)
        singleGear.setPos(gearPoint)
        smallGearExplosion.setPos(gearPoint)
        bigGearExplosion.setPos(gearPoint)
        smallGears.setDepthWrite(False)
        singleGear.setDepthWrite(False)
        smallGearExplosion.setDepthWrite(False)
        bigGearExplosion.setDepthWrite(False)
        if self.isMovingLeftRight:
            self.enterPause()
            suitTrack = Sequence(Func(self.collNodePath.stash), ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit'))
            explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint))
            soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit))
            gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track')
            gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack')
        elif self.isMovingUpDown:

            def getFinalPos():
                if self.isGoingUp:
                    direction = 1.0
                else:
                    direction = -1.0
                pos = Point3(self.deathSuit.getX(), self.deathSuit.getY(), self.deathSuit.getZ() + 2.0 * direction)
                return pos

            deathMoveIval = LerpPosInterval(self.deathSuit, 1.5, pos=getFinalPos(), name='%s-deathSuitMove' % self.suitName, blendType='easeInOut', fluid=1)
            suitTrack = Sequence(Func(self.collNodePath.stash), Parallel(ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), deathMoveIval), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit'))
            explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint))
            soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit))
            gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track')
            gears2MTrack = Track((0.0, explosionTrack), (0.0, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (2.7, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (2.9, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack')

        def removeParticle(particle):
            if particle and hasattr(particle, 'renderParent'):
                particle.cleanup()
                del particle

        removeParticles = Parallel(Func(removeParticle, smallGears), Func(removeParticle, singleGear), Func(removeParticle, smallGearExplosion), Func(removeParticle, bigGearExplosion))
        self.deathTrack = Sequence(Parallel(suitTrack, gears2MTrack, gears1Track, soundTrack), removeParticles, Func(self.destroy))
        self.deathTrack.start()