from direct.distributed.ClockDelta import globalClockDelta
from toontown.toonbase import TTLocalizer
from toontown.parties import PartyGlobals
from toontown.parties.DistributedPartyActivity import DistributedPartyActivity
from toontown.parties.activityFSMs import TeamActivityFSM
from toontown.parties.TeamActivityGui import TeamActivityGui

class DistributedPartyTeamActivity(DistributedPartyActivity):
    notify = directNotify.newCategory('DistributedPartyTeamActivity')

    def __init__(self, cr, activityId, startDelay = PartyGlobals.TeamActivityStartDelay, balanceTeams = False):
        DistributedPartyActivity.__init__(self, cr, activityId, PartyGlobals.ActivityTypes.GuestInitiated, wantRewardGui=True)
        self.notify.debug('__init__')
        self.toonIds = ([], [])
        self.isLocalToonPlaying = False
        self.localToonTeam = None
        self.advantage = 1.0
        self.waitToStartTimestamp = None
        self._maxPlayersPerTeam = 0
        self._minPlayersPerTeam = 0
        self._duration = 0
        self._startDelay = base.config.GetFloat('party-team-activity-start-delay', startDelay)
        self._willBalanceTeams = balanceTeams
        self._currentStatus = ''
        return

    def load(self):
        DistributedPartyActivity.load(self)
        self.teamActivityGui = TeamActivityGui(self)
        self.activityFSM = TeamActivityFSM(self)

    def unload(self):
        DistributedPartyActivity.unload(self)
        del self.activityFSM
        self.teamActivityGui.unload()
        del self.teamActivityGui
        if hasattr(self, 'toonIds'):
            del self.toonIds
        del self.isLocalToonPlaying
        del self.localToonTeam
        del self.advantage
        del self.waitToStartTimestamp

    def handleToonShifted(self, toonId):
        self.notify.error('handleToonShifted( toonId=%d ) must be overriden by child class.' % toonId)

    def handleToonSwitchedTeams(self, toonId):
        if toonId == base.localAvatar.doId and self._canSwitchTeams:
            if self.isState('WaitForEnough') or self.isState('WaitToStart'):
                self.teamActivityGui.enableExitButton()
                self.teamActivityGui.enableSwitchButton()

    def handleToonDisabled(self, toonId):
        self.notify.error('handleToonDisabled( toonId=%d ) must be overriden by child class.' % toonId)

    def setPlayersPerTeam(self, min, max):
        self._minPlayersPerTeam = min
        self._maxPlayersPerTeam = max

    def setDuration(self, duration):
        self._duration = duration

    def setCanSwitchTeams(self, canSwitchTeams):
        self._canSwitchTeams = canSwitchTeams

    def d_toonJoinRequest(self, team):
        if self.isLocalToonInActivity():
            return
        if self.activityFSM.state in ['WaitForEnough', 'WaitToStart'] and self._localToonRequestStatus is None:
            base.cr.playGame.getPlace().fsm.request('activity')
            self.localToonJoining()
            self.sendUpdate('toonJoinRequest', [team])
        return

    def d_toonExitRequest(self):
        toonId = base.localAvatar.doId
        team = self.getTeam(toonId)
        if team is not None:
            if self._localToonRequestStatus is None:
                self.localToonExiting()
                self.sendUpdate('toonExitRequest', [team])
        else:
            self.notify.warning('Not sending exitRequest as localToon has no team.')
        return

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.notify.debug('joinRequestDenied')
        if reason == PartyGlobals.DenialReasons.Full:
            self.showMessage(TTLocalizer.PartyTeamActivityTeamFull)
        elif reason == PartyGlobals.DenialReasons.Default:
            self.showMessage(TTLocalizer.PartyTeamActivityJoinDenied % self.getTitle())

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        if reason == PartyGlobals.DenialReasons.Default:
            self.showMessage(TTLocalizer.PartyTeamActivityExitDenied % self.getTitle())
        if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')):
            self.teamActivityGui.enableExitButton()
            if self._canSwitchTeams:
                self.teamActivityGui.enableSwitchButton()

    def d_toonSwitchTeamRequest(self):
        if not self._canSwitchTeams:
            return
        self.sendUpdate('toonSwitchTeamRequest')

    def switchTeamRequestDenied(self, reason):
        self.notify.debug('switchTeamRequestDenied')
        if reason == PartyGlobals.DenialReasons.Full:
            self.showMessage(TTLocalizer.PartyTeamActivityTeamFull, endState='activity')
        elif reason == PartyGlobals.DenialReasons.Default:
            self.showMessage(TTLocalizer.PartyTeamActivitySwitchDenied, endState='activity')
        if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')) and self._canSwitchTeams:
            self.teamActivityGui.enableSwitchButton()

    def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds):
        newToonIds = [leftTeamToonIds, rightTeamToonIds]
        exitedToons, joinedToons, shifters, switchers = self.getToonsPlayingChanges(self.toonIds, newToonIds)
        if base.localAvatar.doId in exitedToons:
            self.localToonExiting()
        self._processExitedToons(exitedToons)
        self.setToonIds([leftTeamToonIds, rightTeamToonIds])
        self._processJoinedToons(joinedToons)
        for toonId in shifters:
            self.handleToonShifted(toonId)

        if self._canSwitchTeams:
            for toonId in switchers:
                self.handleToonSwitchedTeams(toonId)

        if self.isState('WaitForEnough'):
            self._updateWaitForEnoughStatus()

    def _updateWaitForEnoughStatus(self):
        amount = self.getNumToonsNeededToStart()
        if self._willBalanceTeams:
            text = TTLocalizer.PartyTeamActivityForMoreWithBalance
        else:
            text = TTLocalizer.PartyTeamActivityForMore
        if amount > 1:
            plural = TTLocalizer.PartyTeamActivityForMorePlural
        else:
            plural = ''
        self.setStatus(text % (amount, plural))

    def setState(self, newState, timestamp, data):
        DistributedPartyActivity.setState(self, newState, timestamp)
        if newState == 'WaitToStart':
            self.activityFSM.request(newState, timestamp)
        elif newState == 'Conclusion':
            self.activityFSM.request(newState, data)
        else:
            self.activityFSM.request(newState)

    def d_toonReady(self):
        self.sendUpdate('toonReady')

    def setAdvantage(self, advantage):
        self.advantage = advantage

    def handleToonJoined(self, toonId):
        if toonId == base.localAvatar.doId:
            self.isLocalToonPlaying = True
            self.localToonTeam = self.getTeam(base.localAvatar.doId)
            self.teamActivityGui.load()
            self.teamActivityGui.enableExitButton()
            if self._canSwitchTeams:
                self.teamActivityGui.enableSwitchButton()
            if self.activityFSM.state == 'WaitToStart':
                self.showWaitToStartCountdown()
            else:
                self.showStatus()

    def handleToonExited(self, toonId):
        if toonId == base.localAvatar.doId:
            self.hideStatus()
            self.teamActivityGui.disableExitButton()
            if self._canSwitchTeams:
                self.teamActivityGui.disableSwitchButton()
            if self.activityFSM.state == 'WaitToStart':
                self.hideWaitToStartCountdown()
            self.teamActivityGui.unload()
            self.isLocalToonPlaying = False
            self.localToonTeam = None
        return

    def handleRulesDone(self):
        self.notify.debug('handleRulesDone')
        self.d_toonReady()
        self.activityFSM.request('WaitForServer')

    def handleGameTimerExpired(self):
        pass

    def getToonsPlayingChanges(self, oldToonIds, newToonIds):
        oldLeftTeam = oldToonIds[0]
        oldRightTeam = oldToonIds[1]
        newLeftTeam = newToonIds[0]
        newRightTeam = newToonIds[1]
        oldToons = set(oldLeftTeam + oldRightTeam)
        newToons = set(newLeftTeam + newRightTeam)
        exitedToons = oldToons.difference(newToons)
        joinedToons = newToons.difference(oldToons)
        shifters = []
        if self._canSwitchTeams:
            switchers = list(set(oldLeftTeam) & set(newRightTeam)) + list(set(oldRightTeam) & set(newLeftTeam))
        else:
            switchers = []
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            persistentToons = set(oldToonIds[i]) & set(newToonIds[i])
            for toonId in persistentToons:
                if oldToonIds[i].index(toonId) != newToonIds[i].index(toonId):
                    shifters.append(toonId)

        return (list(exitedToons),
         list(joinedToons),
         shifters,
         switchers)

    def getMaxPlayersPerTeam(self):
        return self._maxPlayersPerTeam

    def getMinPlayersPerTeam(self):
        return self._minPlayersPerTeam

    def getNumToonsNeededToStart(self):
        if self._willBalanceTeams:
            return abs(self._minPlayersPerTeam * 2 - self.getNumToonsPlaying())
        else:
            return self._minPlayersPerTeam

    def getToonIdsAsList(self):
        return self.toonIds[0] + self.toonIds[1]

    def getNumToonsPlaying(self):
        return len(self.toonIds[0]) + len(self.toonIds[1])

    def getNumToonsInTeam(self, team):
        return len(self.toonIds[team])

    def getTeam(self, toonId):
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            if self.toonIds[i].count(toonId) > 0:
                return i
        else:
            return None

        return None

    def getIndex(self, toonId, team):
        if self.toonIds[team].count(toonId) > 0:
            return self.toonIds[team].index(toonId)
        else:
            return None
        return None

    def _joinLeftTeam(self, collEntry):
        if self.isLocalToonInActivity():
            return
        self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam)

    def _joinRightTeam(self, collEntry):
        if self.isLocalToonInActivity():
            return
        self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam)

    def showWaitToStartCountdown(self):
        if self.waitToStartTimestamp is None:
            self.notify.warning('showWaitToStartCountdown was called when self.waitToStartTimestamp was None')
            return
        self.teamActivityGui.showWaitToStartCountdown(self._startDelay, self.waitToStartTimestamp, almostDoneCallback=self._onCountdownAlmostDone)
        self.showStatus()
        self.teamActivityGui.enableExitButton()
        return

    def _onCountdownAlmostDone(self):
        if self._canSwitchTeams:
            self.teamActivityGui.disableSwitchButton()

    def hideWaitToStartCountdown(self):
        self.teamActivityGui.hideWaitToStartCountdown()
        self.teamActivityGui.disableExitButton()
        self.hideStatus()

    def setStatus(self, text):
        self._currentStatus = text
        if self.isLocalToonPlaying:
            self.showStatus()

    def showStatus(self):
        if self.teamActivityGui is not None:
            self.teamActivityGui.showStatus(self._currentStatus)
        return

    def hideStatus(self):
        if self.teamActivityGui is not None:
            self.teamActivityGui.hideStatus()
        return

    def toonsCanSwitchTeams(self):
        return self._canSwitchTeams

    def isState(self, state):
        return hasattr(self, 'activityFSM') and self.activityFSM.getCurrentOrNextState() == state

    def startWaitForEnough(self):
        self.notify.debug('startWaitForEnough')
        self.advantage = 1.0
        self._updateWaitForEnoughStatus()
        if self.isLocalToonPlaying:
            self.teamActivityGui.enableExitButton()
            if self._canSwitchTeams:
                self.teamActivityGui.enableSwitchButton()

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

    def startWaitToStart(self, waitStartTimestamp):
        self.notify.debug('startWaitToStart')
        self.waitToStartTimestamp = globalClockDelta.networkToLocalTime(waitStartTimestamp)
        self._updateWaitForEnoughStatus()
        self.setStatus(TTLocalizer.PartyTeamActivityWaitingToStart)
        if self.isLocalToonPlaying:
            self.showWaitToStartCountdown()

    def finishWaitToStart(self):
        self.notify.debug('finishWaitToStart')
        if self.isLocalToonPlaying:
            self.hideWaitToStartCountdown()
            if self._canSwitchTeams:
                self.teamActivityGui.disableSwitchButton()
        self.waitToStartTimestamp = None
        return

    def startRules(self):
        self.notify.debug('startRules')
        if self.isLocalToonPlaying:
            DistributedPartyActivity.startRules(self)

    def finishRules(self):
        self.notify.debug('finishRules')
        if self.isLocalToonPlaying:
            DistributedPartyActivity.finishRules(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            DistributedPartyActivity.finishRules(self)

    def startWaitForServer(self):
        self.notify.debug('startWaitForServer')
        self.setStatus(TTLocalizer.PartyTeamActivityWaitingForOtherPlayers)

    def finishWaitForServer(self):
        self.notify.debug('finishWaitForServer')
        if self.isLocalToonPlaying:
            self.hideStatus()

    def startActive(self):
        self.notify.debug('startActive')
        if self.isLocalToonPlaying:
            self.hideStatus()
            self.teamActivityGui.showTimer(self._duration)

    def finishActive(self):
        self.notify.debug('finishActive')
        if self.isLocalToonPlaying:
            self.teamActivityGui.hideTimer()
            self.hideStatus()

    def startConclusion(self, data):
        self.notify.debug('startConclusion')
        self.setStatus('')
        if self.isLocalToonPlaying:
            self.localToonExiting()

    def finishConclusion(self):
        self.notify.debug('finishConclusion')
        if self.isLocalToonPlaying:
            self.hideStatus()
