from direct.directnotify import DirectNotifyGlobal
from direct.distributed.ClockDelta import *
from direct.fsm.FSM import FSM
from direct.task import Task
from otp.ai.AIBase import *
from toontown.building import DistributedElevatorFSMAI
from toontown.building import ElevatorConstants
from toontown.toonbase import ToontownGlobals


class DistributedClubElevatorAI(DistributedElevatorFSMAI.DistributedElevatorFSMAI):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloorAI')
    defaultTransitions = {
        'Off': [
            'Opening',
            'Closed'],
        'Opening': [
            'WaitEmpty',
            'WaitCountdown',
            'Opening',
            'Closing'],
        'WaitEmpty': [
            'WaitCountdown',
            'Closing',
            'WaitEmpty'],
        'WaitCountdown': [
            'WaitEmpty',
            'AllAboard',
            'Closing',
            'WaitCountdown'],
        'AllAboard': [
            'WaitEmpty',
            'Closing'],
        'Closing': [
            'Closed',
            'WaitEmpty',
            'Closing',
            'Opening'],
        'Closed': [
            'Opening'] }
    id = 0
    DoBlockedRoomCheck = simbase.config.GetBool('elevator-blocked-rooms-check', 1)

    def __init__(self, air, lawOfficeId, bldg, avIds, markerId = None, numSeats = 4, antiShuffle = 0):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.__init__(self, air, bldg, numSeats, antiShuffle = antiShuffle)
        FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id)
        self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB
        self.countdownTime = ElevatorConstants.ElevatorData[self.type]['countdown']
        self.lawOfficeId = lawOfficeId
        self.anyToonsBailed = 0
        self.avIds = avIds
        self.isEntering = 0
        self.isLocked = 0
        self.setLocked(0)
        self.wantState = None
        self.latchRoom = None
        self.setLatch(markerId)
        self.zoneId = bldg.zoneId

    def generate(self):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.generate(self)

    def generateWithRequired(self, zoneId):
        self.zoneId = zoneId
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.generateWithRequired(self, self.zoneId)

    def delete(self):
        for seatIndex in xrange(len(self.seats)):
            avId = self.seats[seatIndex]
            if avId:
                self.clearFullNow(seatIndex)
                self.clearEmptyNow(seatIndex)
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.delete(self)

    def getEntranceId(self):
        return self.entranceId

    def d_setFloor(self, floorNumber):
        self.sendUpdate('setFloor', [floorNumber])

    def avIsOKToBoard(self, av):
        if av.hp > 0 and self.accepting:
            pass
        return not (self.isLocked)

    def acceptBoarder(self, avId, seatIndex):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.acceptBoarder(self, avId, seatIndex)
        self.acceptOnce(self.air.getAvatarExitEvent(avId), self._DistributedClubElevatorAI__handleUnexpectedExit, extraArgs = [
            avId])
        if self.state == 'WaitEmpty' and self.countFullSeats() < self.countAvsInZone():
            self.request('WaitCountdown')
            self.bldg.elevatorAlert(avId)
        elif self.state in ('WaitCountdown', 'WaitEmpty') and self.countFullSeats() >= self.countAvsInZone():
            taskMgr.doMethodLater(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME, self.goAllAboard, self.quickBoardTask)

    def countAvsInZone(self):
        matchingZones = 0
        for avId in self.bldg.avIds:
            av = self.air.doId2do.get(avId)
            if av:
                if av.zoneId == self.bldg.zoneId:
                    matchingZones += 1
        return matchingZones

    def goAllAboard(self, throwAway = 1):
        self.request('Closing')
        return Task.done

    def _DistributedClubElevatorAI__handleUnexpectedExit(self, avId):
        self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
        seatIndex = self.findAvatar(avId)
        if seatIndex == None:
            pass
        self.clearFullNow(seatIndex)
        self.clearEmptyNow(seatIndex)
        if self.countFullSeats() == 0:
            self.request('WaitEmpty')

    def acceptExiter(self, avId):
        seatIndex = self.findAvatar(avId)
        if seatIndex == None:
            pass
        self.clearFullNow(seatIndex)
        bailFlag = 0
        if self.anyToonsBailed == 0:
            bailFlag = 1
            self.resetCountdown()
            self.anyToonsBailed = 1
        self.sendUpdate('emptySlot' + str(seatIndex), [avId, bailFlag, globalClockDelta.getRealNetworkTime()])
        if self.countFullSeats() == 0:
            self.request('WaitEmpty')
        taskMgr.doMethodLater(ElevatorConstants.TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs = (seatIndex,))

    def enterOpening(self):
        self.d_setState('Opening')
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterOpening(self)
        taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer'))

    def exitOpening(self):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.exitOpening(self)
        if self.isLocked:
            self.wantState = 'closed'
        if self.wantState == 'closed':
            self.demand('Closing')

    def waitEmptyTask(self, task):
        self.request('WaitEmpty')
        return Task.done

    def enterWaitEmpty(self):
        self.lastState = self.state
        for i in xrange(len(self.seats)):
            self.seats[i] = None
        print self.seats
        if self.wantState == 'closed':
            self.demand('Closing')
        else:
            self.d_setState('WaitEmpty')
            self.accepting = 1

    def enterWaitCountdown(self):
        self.lastState = self.state
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterWaitCountdown(self)
        taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
        if self.lastState == 'WaitCountdown':
            pass

    def timeToGoTask(self, task):
        if self.countFullSeats() > 0:
            self.request('AllAboard')
        else:
            self.request('WaitEmpty')
        return Task.done

    def resetCountdown(self):
        taskMgr.remove(self.uniqueName('countdown-timer'))
        taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))

    def enterAllAboard(self):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterAllAboard(self)
        currentTime = globalClock.getRealTime()
        elapsedTime = currentTime - self.timeOfBoarding
        self.notify.debug('elapsed time: ' + str(elapsedTime))
        waitTime = max(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0)
        taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard'))

    def closeTask(self, task):
        if self.countFullSeats() >= 1:
            self.request('Closing')
        else:
            self.request('WaitEmpty')
        return Task.done

    def enterClosing(self):
        if self.countFullSeats() > 0:
            self.sendUpdate('kickToonsOut')
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosing(self)
        taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_STAGE]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer'))
        self.d_setState('Closing')

    def elevatorClosedTask(self, task):
        self.elevatorClosed()
        return Task.done

    def elevatorClosed(self):
        if self.isLocked:
            self.request('Closed')
            return None
        numPlayers = self.countFullSeats()
        if numPlayers > 0:
            players = []
            for i in self.seats:
                if i not in [None, 0]:
                    players.append(i)
            sittingAvIds = []
            for seatIndex in xrange(len(self.seats)):
                avId = self.seats[seatIndex]
                if avId:
                    sittingAvIds.append(avId)
            for avId in self.avIds:
                if avId not in sittingAvIds:
                    continue
            self.bldg.startNextFloor()
        else:
            self.notify.warning('The elevator left, but was empty.')
        self.request('Closed')

    def setLocked(self, locked):
        self.isLocked = locked
        if locked:
            if self.state == 'WaitEmpty':
                self.request('Closing')

            if self.countFullSeats() == 0:
                self.wantState = 'closed'
            else:
                self.wantState = 'opening'
        else:
            self.wantState = 'waitEmpty'
            if self.state == 'Closed':
                self.request('Opening')

    def getLocked(self):
        return self.isLocked

    def unlock(self):
        if self.isLocked:
            self.setLocked(0)

    def lock(self):
        if not self.isLocked:
            self.setLocked(1)

    def start(self):
        self.quickBoardTask = self.uniqueName('quickBoard')
        self.request('Opening')

    def beClosed(self):
        pass

    def setEntering(self, entering):
        self.isEntering = entering

    def getEntering(self):
        return self.isEntering

    def enterClosed(self):
        DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosed(self)
        if self.wantState == 'closed':
            pass
        self.demand('Opening')

    def enterOff(self):
        self.lastState = self.state
        if self.wantState == 'closed':
            self.demand('Closing')
        elif self.wantState == 'waitEmpty':
            self.demand('WaitEmpty')

    def setPos(self, pointPos):
        self.sendUpdate('setPos', [pointPos[0], pointPos[1], pointPos[2]])

    def setH(self, H):
        self.sendUpdate('setH', [H])


    def setLatch(self, markerId):
        self.latch = markerId

    def getLatch(self):
        return self.latch

    def checkBoard(self, av):
        if self.DoBlockedRoomCheck and self.bldg:
            if hasattr(self.bldg, 'blockedRooms'):
                if self.bldg.blockedRooms:
                    return ElevatorConstants.REJECT_BLOCKED_ROOM
        return 0