from direct.directnotify import DirectNotifyGlobal
from toontown.cogdominium.DistCogdoGameAI import DistCogdoGameAI
import CogdoMazeGameGlobals
from direct.distributed.ClockDelta import *
from direct.task import Timer
from toontown.battle import BattleBase
from toontown.building.ElevatorConstants import *

ALL_ABOARD_LAG = 3.7

BASE_TOON_UP = 10
JOKE_TOON_UP = 5

class DistCogdoMazeGameAI(DistCogdoGameAI):
    notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoMazeGameAI")
    delayIntro = BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_FIELD]['openTime']

    def __init__(self, air):
        DistCogdoGameAI.__init__(self, air)
        self.numSuits = (0,0,0)
        self.timer = Timer.Timer()
        self.doorRevealed = False
        self.toonsInDoor = []
        self.bosses = {}
        self.fastMinions = {}
        self.slowMinions = {}
        self.suitTypes = [self.bosses, self.fastMinions, self.slowMinions]
        self.numJokes = {}

    def announceGenerate(self):
        DistCogdoGameAI.announceGenerate(self)
        self.setupSuitsAI()

    def setupSuitsAI(self):
        bossHp = CogdoMazeGameGlobals.SuitData[0]['hp']
        fastMiniHp = CogdoMazeGameGlobals.SuitData[1]['hp']
        slowMiniHp = CogdoMazeGameGlobals.SuitData[2]['hp']

        serialNum = 0
        for i in xrange(self.numSuits[0]):
            self.bosses[serialNum] = bossHp
            serialNum += 1
        for i in xrange(self.numSuits[1]):
            self.fastMinions[serialNum] = fastMiniHp
            serialNum += 1
        for i in xrange(self.numSuits[2]):
            self.slowMinions[serialNum] = slowMiniHp
            serialNum += 1

    def setNumSuits(self, num):
        self.numSuits = num

    def getNumSuits(self):
        return self.numSuits

    def requestUseGag(self, x, y, h, timestamp):
        avId = self.air.getAvatarIdFromSender()
        self.sendUpdate('toonUsedGag', [avId, x, y, h, globalClockDelta.getRealNetworkTime()])

    def requestSuitHitByGag(self, suitType, suitNum):
        hitAI = self.hitSuitAI(suitType, suitNum)
        if not hitAI:
            self.notify.warning('Cannot hit suit!')
            return
        avId = self.air.getAvatarIdFromSender()
        self.sendUpdate('suitHitByGag', [avId, suitType, suitNum])

    def requestHitBySuit(self, suitType, suitNum, nettime):
        avId = self.air.getAvatarIdFromSender()
        av = self.air.doId2do.get(avId)
        if av:
            lostHp = CogdoMazeGameGlobals.SuitData[suitType]['toonDamage'] * self.getDifficulty() * 10
            av.takeDamage(lostHp)
            networkTime = globalClockDelta.getRealNetworkTime()
            self.sendUpdate('toonHitBySuit', [avId, suitType, suitNum, networkTime])
            if av.getHp() < 1:
                self.toonWentSad(avId)

    def requestHitByDrop(self):
        avId = self.air.getAvatarIdFromSender()
        av = self.air.doId2do.get(avId)
        if av:
            lostHp = CogdoMazeGameGlobals.DropDamage
            av.takeDamage(lostHp)
            self.sendUpdate('toonHitByDrop', [avId])

    def requestPickUp(self, pickupNum):
        avId = self.air.getAvatarIdFromSender()
        av = self.air.doId2do.get(avId)
        if av:
            now = globalClockDelta.getRealNetworkTime()

            if avId in self.numJokes:
                self.numJokes[avId] += 1
            else:
                self.numJokes[avId] = 1

            self.sendUpdate('pickUp', [avId, pickupNum, now])

    def requestGag(self, coolerIndex):
        avId = self.air.getAvatarIdFromSender()
        self.sendUpdate('hasGag', [avId, globalClockDelta.getRealNetworkTime()])

    def hitSuitAI(self, suitType, suitNum):
        cogKey = None
        for cogNum in self.suitTypes[suitType].keys():
            if cogNum == suitNum:
                cogKey = cogNum
                break
        if cogKey == None:
            return 0
        cogHp = self.suitTypes[suitType][cogKey]
        cogHp -= 1
        self.suitTypes[suitType][cogKey] = cogHp
        if cogHp <= 0:
            del self.suitTypes[suitType][cogKey]
        return 1

    def handleStart(self):
        taskMgr.add(self.__checkGameDone, self.taskName('check-game-done'))
        taskMgr.add(self.__checkPlayersTask, self.taskName('check-players-task'))
        serverDelay = 1.0
        self.timer.startCallback(CogdoMazeGameGlobals.SecondsUntilTimeout + serverDelay, self.__handleGameOver)
        taskMgr.doMethodLater(serverDelay, self.clientCountdown, self.taskName('client_countdown'))
        taskMgr.add(self.__timeWarningTask, self.taskName('time-warning-task'))

    def clientCountdown(self, task):
        self.doAction(CogdoMazeGameGlobals.GameActions.Countdown, 0)
        return task.done

    def __handleGameOver(self):
        self.removeAll()
        self.gameDone(failed=True)

    def __checkGameDone(self, task):
        bossesLeft = self.bosses
        if len(bossesLeft) == 0:
            self.timer.stop()
            self.doAction(CogdoMazeGameGlobals.GameActions.OpenDoor, 0)
            self.__startTimeout()
            return task.done
        return task.again

    def __startTimeout(self):
        self.timer.startCallback(CogdoMazeGameGlobals.SecondsUntilGameEnds, self.__handleTimeout)

    def __handleTimeout(self):
        for toon in self.toons:
            if not toon in self.toonsInDoor:
                self.killToon(toon)
        self.removeAll()
        self.gameDone()

    def __timeWarningTask(self, task):
        if self.timer.getT() <= CogdoMazeGameGlobals.SecondsForTimeAlert:
            self.doAction(CogdoMazeGameGlobals.GameActions.TimeAlert, 0)
            return task.done
        return task.again

    def killToon(self, avId):
        av = self.air.doId2do.get(avId)
        if av:
            if av.getHp() > 0:
                av.takeDamage(av.getHp())
            self.toonWentSad(avId)
        self.__playerDisconnected(avId)

    def __checkPlayersTask(self, task):
        for toonId in self.toons:
            toon = self.air.doId2do.get(toonId)
            if not toon:
                self.__playerDisconnected(toonId)
        return task.again

    def __playerDisconnected(self, avId):
        self.sendUpdate('setToonDisconnect', [avId])
        self.toons.pop(self.toons.index(avId))
        if len(self.toons) == 0:
            self.removeAll()
            self.gameDone(failed=True)

    def doAction(self, action, data):
        self.sendUpdate('doAction', [action, data, globalClockDelta.getRealNetworkTime()])

    def requestAction(self, action, data):
        Globals = CogdoMazeGameGlobals
        avId = self.air.getAvatarIdFromSender()
        if action == Globals.GameActions.RevealDoor:
            if not self.doorRevealed:
                self.doAction(action, avId)
                self.doorRevealed = True
            else:
                self.notify.warning('Toon tried to reveal door but it\'s already revealed! Ignoring.')
        elif action == Globals.GameActions.EnterDoor:
            if not avId in self.toonsInDoor:
                self.doAction(action, avId)
                self.toonsInDoor.append(avId)
                self.toonUpToon(avId)
            else:
                self.notify.warning('Toon tried to enter into door but already entered! Ignoring.')
                return

            if len(self.toonsInDoor) >= len(self.toons):
                self.__handleAllAboard()
        else:
            self.notify.warning('Client requested unknown action \'%s\'' %action)

    def __handleAllAboard(self):
        if len(self.toonsInDoor) != len(self.toons):
            self.notify.warning('__handleAllAboard expect all toons aboard!')
            return
        self.removeAll()
        taskMgr.doMethodLater(ALL_ABOARD_LAG, lambda t: self.gameDone(), self.taskName('all-aboard-delay'))

    def toonUpToon(self, toonId):
        if toonId in self.toonsInDoor:
            toon = self.air.doId2do.get(toonId)
            if toon:
                val = min(BASE_TOON_UP + JOKE_TOON_UP * self.numJokes.get(toonId, 0), toon.getMaxHp())
                toon.toonUp(val)

    def removeAll(self):
        taskMgr.remove(self.taskName('check-game-done'))
        taskMgr.remove(self.taskName('check-players-task'))
        taskMgr.remove(self.taskName('time-warning-task'))
        taskMgr.remove(self.taskName('all-aboard-delay'))
        self.timer.stop()

    def disable(self):
        DistCogdoGameAI.disable(self)
        self.removeAll()

from otp.ai.MagicWordGlobal import *
@magicWord(category=CATEGORY_PROGRAMMER)
def endMaze():
    if hasattr(simbase.air, 'cogdoGame'):
        maze = simbase.air.cogdoGame
        maze.doAction(CogdoMazeGameGlobals.GameActions.OpenDoor, 0)
        return 'Finished cogdo maze game!'