from direct.distributed import DistributedObjectAI
from direct.distributed.ClockDelta import *
from direct.fsm import ClassicFSM, State
from otp.ai.AIBaseGlobal import *


class DistributedDoorAI(DistributedObjectAI.DistributedObjectAI):
    def __init__(self, air, blockNumber, doorType, doorIndex=0, lockValue=0, swing=3):
        DistributedObjectAI.DistributedObjectAI.__init__(self, air)
        self.block = blockNumber
        self.swing = swing
        self.otherDoor = None
        self.doorType = doorType
        self.doorIndex = doorIndex
        self.setDoorLock(lockValue)
        self.fsm = ClassicFSM.ClassicFSM(
            'DistributedDoorAI_right',
            [
                State.State('off', self.enterOff, self.exitOff,
                            ['closing', 'closed', 'opening', 'open']),
                State.State('closing', self.enterClosing, self.exitClosing,
                            ['closed', 'opening']),
                State.State('closed', self.enterClosed, self.exitClosed,
                            ['opening']),
                State.State('opening', self.enterOpening, self.exitOpening,
                            ['open']),
                State.State('open', self.enterOpen, self.exitOpen,
                            ['closing', 'open'])
            ], 'off', 'off')
        self.fsm.enterInitialState()
        self.exitDoorFSM = ClassicFSM.ClassicFSM(
            'DistributedDoorAI_left',
                [
                    State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff,
                                ['closing', 'closed', 'opening', 'open']),
                    State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing,
                                ['closed', 'opening']),
                    State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed,
                                ['opening']),
                    State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening,
                                ['open']),
                    State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen,
                                ['closing', 'open'])
                ], 'off', 'off')
        self.exitDoorFSM.enterInitialState()
        self.doLaterTask = None
        self.exitDoorDoLaterTask = None
        self.avatarsWhoAreEntering = {}
        self.avatarsWhoAreExiting = {}

    def delete(self):
        taskMgr.remove(self.uniqueName('door_opening-timer'))
        taskMgr.remove(self.uniqueName('door_open-timer'))
        taskMgr.remove(self.uniqueName('door_closing-timer'))
        taskMgr.remove(self.uniqueName('exit_door_open-timer'))
        taskMgr.remove(self.uniqueName('exit_door_closing-timer'))
        taskMgr.remove(self.uniqueName('exit_door_opening-timer'))
        self.ignoreAll()
        del self.block
        del self.swing
        del self.doorType
        del self.fsm
        del self.exitDoorFSM
        del self.otherDoor
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def getDoorIndex(self):
        return self.doorIndex

    def setSwing(self, swing):
        self.swing = swing

    def getSwing(self):
        return self.swing

    def getDoorType(self):
        return self.doorType

    def getZoneIdAndBlock(self):
        return [self.zoneId, self.block]

    def setOtherDoor(self, door):
        self.otherDoor = door

    def getZoneId(self):
        return self.zoneId

    def getState(self):
        return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]

    def getExitDoorState(self):
        return [self.exitDoorFSM.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]

    def isOpen(self):
        state = self.fsm.getCurrentState().getName()
        return state == 'open' or state == 'opening'

    def isClosed(self):
        return not self.isOpen()

    def setDoorLock(self, locked):
        self.lockedDoor = locked

    def isLockedDoor(self):
        if simbase.config.GetBool('no-locked-doors', 0):
            return 0
        else:
            return self.lockedDoor

    def sendReject(self, avatarID, lockedVal):
        self.sendUpdateToAvatarId(avatarID, 'rejectEnter', [lockedVal])

    def requestEnter(self):
        avatarID = self.air.getAvatarIdFromSender()
        lockedVal = self.isLockedDoor()
        if lockedVal:
            self.sendReject(avatarID, lockedVal)
        else:
            self.enqueueAvatarIdEnter(avatarID)
            self.sendUpdateToAvatarId(
                avatarID, 'setOtherZoneIdAndDoId',
                [self.otherDoor.getZoneId(), self.otherDoor.getDoId()])

    def enqueueAvatarIdEnter(self, avatarID):
        if avatarID not in self.avatarsWhoAreEntering:
            self.avatarsWhoAreEntering[avatarID] = 1
            self.sendUpdate('avatarEnter', [avatarID])
        self.openDoor(self.fsm)

    def openDoor(self, doorFsm):
        stateName = doorFsm.getCurrentState().getName()
        if stateName == 'open':
            doorFsm.request('open')
        elif stateName != 'opening':
            doorFsm.request('opening')

    def requestExit(self):
        avatarID = self.air.getAvatarIdFromSender()
        self.sendUpdate('avatarExit', [avatarID])
        self.enqueueAvatarIdExit(avatarID)

    def enqueueAvatarIdExit(self, avatarID):
        if avatarID in self.avatarsWhoAreEntering:
            del self.avatarsWhoAreEntering[avatarID]
        elif avatarID not in self.avatarsWhoAreExiting:
            self.avatarsWhoAreExiting[avatarID] = 1
            self.openDoor(self.exitDoorFSM)

    def requestSuitEnter(self, avatarID):
        self.enqueueAvatarIdEnter(avatarID)

    def requestSuitExit(self, avatarID):
        self.sendUpdate('avatarExit', [avatarID])
        self.enqueueAvatarIdExit(avatarID)

    def d_setState(self, state):
        self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])

    def d_setExitDoorState(self, state):
        self.sendUpdate('setExitDoorState', [state, globalClockDelta.getRealNetworkTime()])

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def openTask(self, task):
        self.fsm.request('closing')
        return Task.done

    def enterClosing(self):
        self.d_setState('closing')
        self.doLaterTask = taskMgr.doMethodLater(1, self.closingTask, self.uniqueName('door_closing-timer'))

    def exitClosing(self):
        if self.doLaterTask:
            taskMgr.remove(self.doLaterTask)
            self.doLaterTask = None

    def closingTask(self, task):
        self.fsm.request('closed')
        return Task.done

    def enterClosed(self):
        self.d_setState('closed')

    def exitClosed(self):
        pass

    def enterOpening(self):
        self.d_setState('opening')
        self.doLaterTask = taskMgr.doMethodLater(
            1, self.openingTask, self.uniqueName('door_opening-timer'))

    def exitOpening(self):
        if self.doLaterTask:
            taskMgr.remove(self.doLaterTask)
            self.doLaterTask = None

    def openingTask(self, task):
        self.fsm.request('open')
        return Task.done

    def enterOpen(self):
        self.d_setState('open')
        self.avatarsWhoAreEntering = {}
        self.doLaterTask = taskMgr.doMethodLater(
            1, self.openTask, self.uniqueName('door_open-timer'))

    def exitOpen(self):
        if self.doLaterTask:
            taskMgr.remove(self.doLaterTask)
            self.doLaterTask = None

    def exitDoorEnterOff(self):
        pass

    def exitDoorExitOff(self):
        pass

    def exitDoorOpenTask(self, task):
        self.exitDoorFSM.request('closing')
        return Task.done

    def exitDoorEnterClosing(self):
        self.d_setExitDoorState('closing')
        self.exitDoorDoLaterTask = taskMgr.doMethodLater(
            1, self.exitDoorClosingTask, self.uniqueName('exit_door_closing-timer'))

    def exitDoorExitClosing(self):
        if self.exitDoorDoLaterTask:
            taskMgr.remove(self.exitDoorDoLaterTask)
            self.exitDoorDoLaterTask = None

    def exitDoorClosingTask(self, task):
        self.exitDoorFSM.request('closed')
        return Task.done

    def exitDoorEnterClosed(self):
        self.d_setExitDoorState('closed')

    def exitDoorExitClosed(self):
        if self.exitDoorDoLaterTask:
            taskMgr.remove(self.exitDoorDoLaterTask)
            self.exitDoorDoLaterTask = None

    def exitDoorEnterOpening(self):
        self.d_setExitDoorState('opening')
        self.exitDoorDoLaterTask = taskMgr.doMethodLater(
            1, self.exitDoorOpeningTask, self.uniqueName('exit_door_opening-timer'))

    def exitDoorExitOpening(self):
        if self.exitDoorDoLaterTask:
            taskMgr.remove(self.exitDoorDoLaterTask)
            self.exitDoorDoLaterTask = None

    def exitDoorOpeningTask(self, task):
        self.exitDoorFSM.request('open')
        return Task.done

    def exitDoorEnterOpen(self):
        self.d_setExitDoorState('open')
        self.avatarsWhoAreExiting = { }
        self.exitDoorDoLaterTask = taskMgr.doMethodLater(
            1, self.exitDoorOpenTask, self.uniqueName('exit_door_open-timer'))

    def exitDoorExitOpen(self):
        if self.exitDoorDoLaterTask:
            taskMgr.remove(self.exitDoorDoLaterTask)
            self.exitDoorDoLaterTask = None