from panda3d.core import *
from direct.interval.IntervalGlobal import *
from direct.distributed.ClockDelta import *
from toontown.toonbase import ToontownGlobals
from direct.directnotify import DirectNotifyGlobal
import DistributedDoorEntityBase
from direct.fsm import FourState
from direct.fsm import ClassicFSM
from otp.level import DistributedEntity
from toontown.toonbase import TTLocalizer
from otp.level import BasicEntities
from direct.fsm import State
from otp.level import VisibilityBlocker
from panda3d.direct import HideInterval, ShowInterval

class DistributedDoorEntityLock(DistributedDoorEntityBase.LockBase, FourState.FourState):
    slideLeft = Vec3(-7.5, 0.0, 0.0)
    slideRight = Vec3(7.5, 0.0, 0.0)

    def __init__(self, door, lockIndex, lockedNodePath, leftNodePath, rightNodePath, stateIndex):
        self.door = door
        self.lockIndex = lockIndex
        self.lockedNodePath = lockedNodePath
        self.leftNodePath = leftNodePath
        self.rightNodePath = rightNodePath
        self.initialStateIndex = stateIndex
        FourState.FourState.__init__(self, self.stateNames, self.stateDurations)

    def delete(self):
        self.takedown()
        del self.door

    def setup(self):
        self.setLockState(self.initialStateIndex)
        del self.initialStateIndex

    def takedown(self):
        if self.track is not None:
            self.track.pause()
            self.track = None
        for i in self.states.keys():
            del self.states[i]

        self.states = []
        self.fsm = None
        return

    def setLockState(self, stateIndex):
        if self.stateIndex != stateIndex:
            state = self.states.get(stateIndex)
            if state is not None:
                self.fsm.request(state)
        return

    def isUnlocked(self):
        return self.isOn()

    def enterState1(self):
        FourState.FourState.enterState1(self)
        beat = self.duration * 0.05
        slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.ogg')
        self.setTrack(Sequence(Wait(beat * 2.0), Parallel(SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath), ShowInterval(self.lockedNodePath)))))

    def enterState2(self):
        FourState.FourState.enterState2(self)
        self.setTrack(None)
        self.leftNodePath.setPos(self.lockedNodePath, Vec3(0.0))
        self.rightNodePath.setPos(self.lockedNodePath, Vec3(0.0))
        self.leftNodePath.hide()
        self.rightNodePath.hide()
        self.lockedNodePath.show()
        return

    def enterState3(self):
        FourState.FourState.enterState3(self)
        unlockSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_unlock.ogg')
        slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.ogg')
        beat = self.duration * 0.05
        self.setTrack(Sequence(Wait(beat * 2), Parallel(SoundInterval(unlockSfx, node=self.door.node, volume=0.8), SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(HideInterval(self.lockedNodePath), ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideLeft, blendType='easeOut'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideRight, blendType='easeOut')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath)))))

    def enterState4(self):
        FourState.FourState.enterState4(self)
        self.setTrack(None)
        self.leftNodePath.setPos(self.lockedNodePath, self.slideLeft)
        self.rightNodePath.setPos(self.lockedNodePath, self.slideRight)
        self.leftNodePath.hide()
        self.rightNodePath.hide()
        self.lockedNodePath.hide()
        return


class DistributedDoorEntity(DistributedDoorEntityBase.DistributedDoorEntityBase, DistributedEntity.DistributedEntity, BasicEntities.NodePathAttribsProxy, FourState.FourState, VisibilityBlocker.VisibilityBlocker):

    def __init__(self, cr):
        self.innerDoorsTrack = None
        self.isVisReady = 0
        self.isOuterDoorOpen = 0
        DistributedEntity.DistributedEntity.__init__(self, cr)
        FourState.FourState.__init__(self, self.stateNames, self.stateDurations)
        VisibilityBlocker.VisibilityBlocker.__init__(self)
        self.locks = []
        return

    def generate(self):
        DistributedEntity.DistributedEntity.generate(self)

    def announceGenerate(self):
        self.doorNode = hidden.attachNewNode('door-%s' % self.entId)
        DistributedEntity.DistributedEntity.announceGenerate(self)
        BasicEntities.NodePathAttribsProxy.initNodePathAttribs(self)
        self.setup()

    def disable(self):
        self.takedown()
        self.doorNode.removeNode()
        del self.doorNode
        DistributedEntity.DistributedEntity.disable(self)

    def delete(self):
        DistributedEntity.DistributedEntity.delete(self)

    def setup(self):
        self.setupDoor()
        for i in self.locks:
            i.setup()

        self.accept('exit%s' % (self.getName(),), self.exitTrigger)
        self.acceptAvatar()
        if __dev__:
            self.initWantDoors()

    def takedown(self):
        if __dev__:
            self.shutdownWantDoors()
        self.ignoreAll()
        if self.track is not None:
            self.track.finish()
        self.track = None
        if self.innerDoorsTrack is not None:
            self.innerDoorsTrack.finish()
        self.innerDoorsTrack = None
        for i in self.locks:
            i.takedown()

        self.locks = []
        self.fsm = None
        for i in self.states.keys():
            del self.states[i]

        self.states = []
        return

    setUnlock0Event = DistributedDoorEntityBase.stubFunction
    setUnlock1Event = DistributedDoorEntityBase.stubFunction
    setUnlock2Event = DistributedDoorEntityBase.stubFunction
    setUnlock3Event = DistributedDoorEntityBase.stubFunction
    setIsOpenEvent = DistributedDoorEntityBase.stubFunction
    setIsLock0Unlocked = DistributedDoorEntityBase.stubFunction
    setIsLock1Unlocked = DistributedDoorEntityBase.stubFunction
    setIsLock2Unlocked = DistributedDoorEntityBase.stubFunction
    setIsLock3Unlocked = DistributedDoorEntityBase.stubFunction
    setIsOpen = DistributedDoorEntityBase.stubFunction
    setSecondsOpen = DistributedDoorEntityBase.stubFunction

    def acceptAvatar(self):
        self.accept('enter%s' % (self.getName(),), self.enterTrigger)

    def rejectInteract(self):
        DistributedEntity.DistributedEntity.rejectInteract(self)
        self.acceptAvatar()

    def avatarExit(self, avatarId):
        DistributedEntity.DistributedEntity.avatarExit(self, avatarId)
        self.acceptAvatar()

    def enterTrigger(self, args=None):
        messenger.send('DistributedInteractiveEntity_enterTrigger')
        self.sendUpdate('requestOpen')

    def exitTrigger(self, args=None):
        messenger.send('DistributedInteractiveEntity_exitTrigger')

    def okToUnblockVis(self):
        VisibilityBlocker.VisibilityBlocker.okToUnblockVis(self)
        self.isVisReady = 1
        self.openInnerDoors()

    def changedOnState(self, isOn):
        messenger.send(self.getOutputEventName(), [not isOn])

    def setLocksState(self, stateBits):
        lock0 = stateBits & 15
        lock1 = (stateBits & 240) >> 4
        lock2 = (stateBits & 3840) >> 8
        if self.isGenerated():
            self.locks[0].setLockState(lock0)
            self.locks[1].setLockState(lock1)
            self.locks[2].setLockState(lock2)
        else:
            self.initialLock0StateIndex = lock0
            self.initialLock1StateIndex = lock1
            self.initialLock2StateIndex = lock2

    def setDoorState(self, stateIndex, timeStamp):
        self.stateTime = globalClockDelta.localElapsedTime(timeStamp)
        if self.isGenerated():
            if self.stateIndex != stateIndex:
                state = self.states.get(stateIndex)
                if state is not None:
                    self.fsm.request(state)
        else:
            self.initialState = stateIndex
            self.initialStateTimestamp = timeStamp
        return

    def getName(self):
        return 'switch-%s' % str(self.entId)

    def getNodePath(self):
        if hasattr(self, 'doorNode'):
            return self.doorNode
        else:
            return

    def setupDoor(self):
        model = loader.loadModel('phase_9/models/cogHQ/CogDoorHandShake')
        if model:
            doorway = model.find('**/Doorway1')
            rootNode = self.doorNode.attachNewNode(self.getName() + '-root')
            rootNode.setPos(self.pos)
            rootNode.setHpr(self.hpr)
            rootNode.setScale(self.scale)
            rootNode.setColor(self.color)
            change = rootNode.attachNewNode('changePos')
            doorway.reparentTo(change)
            self.node = rootNode
            self.node.show()
            self.locks.append(DistributedDoorEntityLock(self, 0, doorway.find('**/Slide_One_Closed'), doorway.find('**/Slide_One_Left_Open'), doorway.find('**/Slide_One_Right_Open'), self.initialLock0StateIndex))
            self.locks.append(DistributedDoorEntityLock(self, 1, doorway.find('**/Slide_Two_Closed'), doorway.find('**/Slide_Two_Left_Open'), doorway.find('**/Slide_Two_Right_Open'), self.initialLock1StateIndex))
            self.locks.append(DistributedDoorEntityLock(self, 2, doorway.find('**/Slide_Three_Closed'), doorway.find('**/Slide_Three_Left_Open'), doorway.find('**/Slide_Three_Right_Open'), self.initialLock2StateIndex))
            del self.initialLock0StateIndex
            del self.initialLock1StateIndex
            del self.initialLock2StateIndex
            door = doorway.find('doortop')
            if door.isEmpty():
                print 'doortop hack'
                door = doorway.attachNewNode('doortop')
                doorway.find('doortop1').reparentTo(door)
                doorway.find('doortop2').reparentTo(door)
            rootNode = self.doorNode.attachNewNode(self.getName() + '-topDoor')
            rootNode.setPos(self.pos)
            rootNode.setHpr(self.hpr)
            rootNode.setScale(self.scale)
            rootNode.setColor(self.color)
            change = rootNode.attachNewNode('changePos')
            door.reparentTo(change)
            self.doorTop = rootNode
            self.doorTop.show()
            rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-leftDoor')
            change = rootNode.attachNewNode('change')
            door = doorway.find('**/doorLeft')
            door = door.reparentTo(change)
            self.doorLeft = rootNode
            self.doorLeft.show()
            change.setPos(self.pos)
            change.setHpr(self.hpr)
            change.setScale(self.scale)
            change.setColor(self.color)
            door = doorway.find('doorbottom')
            if door.isEmpty():
                print 'doorbottom hack'
                door = doorway.attachNewNode('doorbottom')
                doorway.find('doorbottom1').reparentTo(door)
                doorway.find('doorbottom2').reparentTo(door)
            change = render.attachNewNode('changePos')
            door.reparentTo(change)
            rootNode = self.doorNode.attachNewNode(self.getName() + '-bottomDoor')
            rootNode.setPos(self.pos)
            rootNode.setHpr(self.hpr)
            rootNode.setScale(self.scale)
            rootNode.setColor(self.color)
            change.reparentTo(rootNode)
            self.doorBottom = rootNode
            self.doorBottom.show()
            rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-rightDoor')
            change = rootNode.attachNewNode('change')
            door = doorway.find('**/doorRight')
            door = door.reparentTo(change)
            self.doorRight = rootNode
            self.doorRight.show()
            change.setPos(self.pos)
            change.setHpr(self.hpr)
            change.setScale(self.scale)
            change.setColor(self.color)
            collision = self.doorLeft.find('**/doorLeft_collision1')
            collision.setName(self.getName())
            collision = self.doorLeft.find('**/doorLeft_collision2')
            collision.setName(self.getName())
            collision = self.doorRight.find('**/doorRight_collision1')
            collision.setName(self.getName())
            collision = self.doorRight.find('**/doorRight_collision2')
            collision.setName(self.getName())
            collision = self.doorLeft.find('**/doorLeft_innerCollision')
            collision.setName(self.getName())
            self.leftInnerCollision = collision
            collision = self.doorRight.find('**/doorRight_innerCollision')
            collision.setName(self.getName())
            self.rightInnerCollision = collision
            self.node.flattenMedium()
            self.doorTop.flattenMedium()
            self.doorBottom.flattenMedium()
            self.doorLeft.flattenMedium()
            self.doorRight.flattenMedium()
        self.setDoorState(self.initialState, self.initialStateTimestamp)
        del self.initialState
        del self.initialStateTimestamp

    def setInnerDoorsTrack(self, track):
        if self.innerDoorsTrack is not None:
            self.innerDoorsTrack.pause()
            self.innerDoorsTrack = None
        if track is not None:
            track.start(0.0)
            self.innerDoorsTrack = track
        return

    def openInnerDoors(self):
        print 'openInnerDoors'
        if not self.level.complexVis() or self.isOuterDoorOpen and (not self.isVisBlocker or self.isVisReady):
            print 'openInnerDoors stage Two'
            duration = self.duration
            slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg')
            finalSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg')
            moveDistance = 8.0
            self.setInnerDoorsTrack(Sequence(Func(self.leftInnerCollision.unstash), Func(self.rightInnerCollision.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(-moveDistance, 0.0, 0.0), blendType='easeOut'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(moveDistance, 0.0, 0.0), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorLeft.stash), Func(self.doorRight.stash)))

    def closeInnerDoors(self):
        duration = self.duration
        slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg')
        finalSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg')
        moveDistance = 8.0
        self.setInnerDoorsTrack(Sequence(Func(self.doorLeft.unstash), Func(self.doorRight.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.leftInnerCollision.stash), Func(self.rightInnerCollision.stash)))

    def setisOuterDoorOpen(self, isOpen):
        self.isOuterDoorOpen = isOpen

    def enterState1(self):
        print 'doors enter state 1'
        FourState.FourState.enterState1(self)
        self.isOuterDoorOpen = 0
        if self.isVisBlocker:
            if not self.isVisReady:
                self.requestUnblockVis()
        else:
            self.okToUnblockVis()
        duration = self.duration
        slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg')
        finalSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg')
        moveDistance = 8.0
        self.setTrack(Sequence(Wait(duration * 0.1), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorTop, duration=duration * 0.4, pos=Vec3(0.0, 0.0, moveDistance), blendType='easeOut'), LerpPosInterval(nodePath=self.doorBottom, duration=duration * 0.4, pos=Vec3(0.0, 0.0, -moveDistance), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorTop.stash), Func(self.doorBottom.stash), Func(self.setisOuterDoorOpen, 1), Func(self.openInnerDoors)))

    def enterState2(self):
        FourState.FourState.enterState2(self)
        self.isOuterDoorOpen = 1
        self.setTrack(None)
        moveDistance = 7.5
        (self.doorTop.setPos(Vec3(0.0, 0.0, moveDistance)),)
        (self.doorBottom.setPos(Vec3(0.0, 0.0, -moveDistance)),)
        self.doorTop.stash()
        self.doorBottom.stash()
        if not self.isVisBlocker or not self.isWaitingForUnblockVis():
            self.setInnerDoorsTrack(None)
            self.doorLeft.setPos(Vec3(-moveDistance, 0.0, 0.0))
            self.doorRight.setPos(Vec3(moveDistance, 0.0, 0.0))
            self.doorLeft.stash()
            self.doorRight.stash()
        return

    def exitState2(self):
        FourState.FourState.exitState2(self)
        self.cancelUnblockVis()

    def enterState3(self):
        FourState.FourState.enterState3(self)
        duration = self.duration
        slideSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg')
        finalSfx = base.loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg')
        self.setTrack(Sequence(Wait(duration * 0.1), Func(self.closeInnerDoors), Wait(duration * 0.4), Func(self.doorTop.unstash), Func(self.doorBottom.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorTop, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.doorBottom, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=duration * 0.4, volume=0.8))), Func(self.setisOuterDoorOpen, 0)))

    def enterState4(self):
        FourState.FourState.enterState4(self)
        self.setisOuterDoorOpen(0)
        self.isVisReady = 0
        self.setTrack(None)
        self.doorTop.unstash()
        self.doorBottom.unstash()
        self.doorTop.setPos(Vec3(0.0))
        self.doorBottom.setPos(Vec3(0.0))
        self.setInnerDoorsTrack(None)
        self.leftInnerCollision.stash()
        self.rightInnerCollision.stash()
        self.doorLeft.unstash()
        self.doorRight.unstash()
        self.doorLeft.setPos(Vec3(0.0))
        self.doorRight.setPos(Vec3(0.0))
        return

    if __dev__:

        def initWantDoors(self):
            self.accept('wantDoorsChanged', self.onWantDoorsChanged)
            self.onWantDoorsChanged()

        def shutdownWantDoors(self):
            self.ignore('wantDoorsChanged')

        def onWantDoorsChanged(self):
            if self.level.levelMgrEntity.wantDoors:
                self.getNodePath().unstash()
            else:
                self.getNodePath().stash()

        def attribChanged(self, attrib, value):
            self.takedown()
            self.setup()