from direct.directnotify import DirectNotifyGlobal
from direct.distributed.ClockDelta import *
from direct.distributed.DistributedObjectAI import DistributedObjectAI
from direct.fsm.FSM import FSM
from direct.task import Task
import random

from toontown.racing import RaceGlobals
from toontown.racing.DistributedGagAI import DistributedGagAI
from toontown.racing.DistributedVehicleAI import DistributedVehicleAI
from toontown.toonbase import TTLocalizer


class DistributedRaceAI(DistributedObjectAI, FSM):
    notify = DirectNotifyGlobal.directNotify.newCategory("DistributedRaceAI")

    def __init__(self, air):
        DistributedObjectAI.__init__(self, air)
        FSM.__init__(self, 'DistributedRaceAI')
        self.air = air
        self.zoneId = 0
        self.trackId = 0
        self.raceType = 0
        self.circuitLoop = []
        self.avatars = []
        self.finishedAvatars = []
        self.startingPlaces = []
        self.avatarKarts = []
        self.lapCount = 1
        self.gags = {}
        self.avatarGags = {}
        self.livingGags = []
        self.currentlyAffectedByAnvil = {}
        self.avatarProgress = {}

    def generate(self):
        for avatar in self.avatars:
            self.acceptOnce(self.air.getAvatarExitEvent(avatar), self.playerLeave, [avatar])
        self.demand('Join')

    def delete(self):
        for aK in self.avatarKarts:
            kart = self.air.doId2do[aK[1]]
            kart.requestDelete()
        for gag in self.livingGags:
            gag.requestDelete()
        self.air.deallocateZone(self.zoneId)
        for i in xrange(len(self.gags)):
            taskMgr.remove('regenGag%i-%i' % (i, self.doId))
        DistributedObjectAI.delete(self)

    def enterJoin(self):
        self.beginBarrier('waitingForJoin', self.avatars, 60, self.joinBarrierCallback)
        self.d_waitingForJoin()

    def exitJoin(self):
        pass

    def enterPrep(self):
        self.beginBarrier('waitingForReady', self.avatars, 60, self.readyBarrierCallback)
        self.gagPoints = RaceGlobals.TrackDict[self.trackId][4]
        if self.raceType != RaceGlobals.Practice:
            for i in xrange(len(self.gagPoints)):
                gagId = random.randint(0, 5)
                self.b_genGag(i, 1, gagId)
        self.d_prepForRace()

    def exitPrep(self):
        pass

    def enterTutorial(self):
        self.beginBarrier('readRules', self.avatars, 30, self.readRulesCallback)
        self.d_startTutorial()

    def exitTutorial(self):
        pass

    def enterStart(self):
        self.startTime = globalClockDelta.networkToLocalTime(globalClockDelta.getRealNetworkTime()) + 3
        self.b_startRace(3)

    def exitStart(self):
        pass

    def readyBarrierCallback(self, avatars):
        self.demand('Tutorial')

    def readRulesCallback(self, avatars):
        self.demand('Start')

    def joinBarrierCallback(self, avatars):
        for av in self.avatars:
            if not av in avatars:
                self.playerLeave(av)
        for av in avatars:
            kart = DistributedVehicleAI(self.air, av)
            kart.generateWithRequired(self.zoneId)
            self.avatarKarts.append([av, kart.getDoId()])
        self.beginBarrier('waitingForPrep', self.avatars, 60, self.prepBarrierCallback)
        self.sendUpdate('setEnteredRacers', [self.avatarKarts])

    def prepBarrierCallback(self, avatars):
        self.demand('Prep')

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def d_setZoneId(self, zoneId):
        self.sendUpdate('setZoneId', [zoneId])

    def b_setZoneId(self, zoneId):
        self.setZoneId(zoneId)
        self.d_setZoneId(zoneId)

    def getZoneId(self):
        return self.zoneId

    def setTrackId(self, trackId):
        self.trackId = trackId

    def getTrackId(self):
        return self.trackId

    def setRaceType(self, raceType):
        self.raceType = raceType

    def getRaceType(self):
        return self.raceType

    def setCircuitLoop(self, circuitLoop):
        self.circuitLoop = circuitLoop

    def getCircuitLoop(self):
        return self.circuitLoop

    def setAvatars(self, avatarList):
        self.avatars = avatarList

    def getAvatars(self):
        return self.avatars

    def setStartingPlaces(self, startingPlaces):
        self.startingPlaces = startingPlaces

    def getStartingPlaces(self):
        return self.startingPlaces

    def setLapCount(self, lapCount):
        self.lapCount = lapCount

    def getLapCount(self):
        return self.lapCount

    def waitingForJoin(self):
        self.beginBarrier('waitingForJoin', self.avatars, 60, self.b_prepForRace)

    def d_waitingForJoin(self):
        self.sendUpdate('waitingForJoin', [])

    def b_waitingForJoin(self):
        self.waitingForJoin()
        self.d_waitingForJoin()

    def setEnteredRacers(self, todo0):
        pass

    def d_prepForRace(self):
        self.sendUpdate('prepForRace', [])

    def b_prepForRace(self, avatars):
        self.prepForRace()
        self.d_prepForRace()

    def startTutorial(self):
        self.beginBarrier('readRules', self.avatars, 60, self.raceStart)

    def d_startTutorial(self):
        self.sendUpdate('startTutorial', [])

    def b_startTutorial(self, avatars):
        self.startTutorial()
        self.d_startTutorial()

    def startRace(self, timeUntilStart):
        taskMgr.doMethodLater(timeUntilStart, self.startKarts, 'startKarts%i' % self.doId, [])

    def startKarts(self):
        for avatarKart in self.avatarKarts:
            if avatarKart[1] in self.air.doId2do:
                kart = self.air.doId2do[avatarKart[1]]
                kart.sendUpdate('setInput', [1])
                self.avatarProgress[avatarKart[0]] = 0
                self.avatarGags[avatarKart[0]] = 0
                self.currentlyAffectedByAnvil[avatarKart[0]]  = False

    def b_startRace(self, timeUntilStart):
        self.startRace(timeUntilStart)
        self.d_startRace(timeUntilStart)

    def d_startRace(self, timeUntilStart):
        self.sendUpdate('startRace', [globalClockDelta.localToNetworkTime(globalClockDelta.globalClock.getRealTime() + timeUntilStart)])

    def goToSpeedway(self, todo0, todo1):
        pass

    def genGag(self, slot, number, type):
        self.gags[slot] = [number, type]

    def d_genGag(self, slot, number, type):
        self.sendUpdate('genGag', [slot, number, type])

    def b_genGag(self, slot, number, type):
        self.genGag(slot, number, type)
        self.d_genGag(slot, number, type)

    def dropAnvilOn(self, todo0, todo1, todo2):
        pass

    def shootPiejectile(self, todo0, todo1, todo2):
        pass

    def racerDisconnected(self, todo0):
        pass

    def setPlace(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9):
        pass

    def setCircuitPlace(self, todo0, todo1, todo2, todo3, todo4, todo5):
        pass

    def endCircuitRace(self):
        pass

    def setRaceZone(self, todo0, todo1):
        pass

    def hasGag(self, slot, requestedGag, index):
        avId = self.air.getAvatarIdFromSender()
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to get gag in a race they\'re not in!')
            return
        if self.raceType == RaceGlobals.Practice:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to gain gag in a practice race!')
            return
        places = sorted(self.avatarProgress, key=self.avatarProgress.get)
        avPlace = places.index(avId)
        gag = self.gags[slot]
        if not gag[0]:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to pick up a gag that doesn\'t exist!')
            return
        gagIndex = gag[1]
        realGag = RaceGlobals.GagFreq[avPlace][gagIndex]
        if realGag != requestedGag:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to get the wrong gag!')
            return
        self.gags[slot] = [0, 0]
        self.avatarGags[avId] = requestedGag
        taskMgr.doMethodLater(5, self.__regenGag, 'regenGag%i-%i' % (slot, self.doId), [slot])

    def __regenGag(self, index):
        gagId = random.randint(0, 5)
        self.b_genGag(index, 1, gagId)

    def racerLeft(self, avId):
        realAvId = self.air.getAvatarIdFromSender()
        if realAvId != avId:
            self.air.writeServerEvent('suspicious', realAvId, 'Toon tried to make another quit race!')
            return
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to leave race they\'re not in!')
            return
        self.avatars.remove(avId)
        if set(self.finishedAvatars) == set(self.avatars) or len(self.avatars) == 0:
            self.requestDelete()

    def heresMyT(self, avId, laps, currentLapT, timestamp):
        realAvId = self.air.getAvatarIdFromSender()
        if not avId == realAvId:
            self.air.writeServerEvent('suspicious', realAvId, 'Toon tried to send a message as another toon!')
            return
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon not in race tried to send update to race!')
            return
        if laps == self.lapCount:
            self.avatarFinished(avId)
        self.avatarProgress[avId] = laps + currentLapT

    def avatarFinished(self, avId):
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to finish in a race they\'re not in!')
            return

        if avId in self.finishedAvatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to finish in a race twice!')
            return
        self.finishedAvatars.append(avId)

        av = self.air.doId2do.get(avId)
        place = len(self.finishedAvatars)
        entryFee = RaceGlobals.getEntryFee(self.trackId, self.raceType)
        bonus = 0
        totalTime = globalClockDelta.networkToLocalTime(globalClockDelta.getRealNetworkTime()) - self.startTime
        qualify = False
        if totalTime < RaceGlobals.getQualifyingTime(self.trackId):
            qualify = True
        if self.raceType == RaceGlobals.Practice:
            winnings = RaceGlobals.PracticeWinnings
            trophies = []
        elif qualify:
            offset = 4 - len(self.avatarProgress) # self.avatarProgress contains the amount of STARTING players.
            winnings = entryFee * RaceGlobals.Winnings[(place+offset)-1]
            trophies = self.calculateTrophies(avId, place == 1, qualify, totalTime)
        else:
            winnings = 0
            trophies = []
        av.b_setTickets(av.getTickets() + winnings)
        if av.getTickets() > RaceGlobals.MaxTickets:
            av.b_setTickets(RaceGlobals.MaxTickets)
        self.sendUpdate('setPlace', [avId, totalTime, place, entryFee, qualify, max((winnings-entryFee), 0), bonus, trophies, [], 0])

    def calculateTrophies(self, avId, won, qualify, time):
        av = self.air.doId2do[avId]
        kartingHistory = av.getKartingHistory()
        avTrophies = av.getKartingTrophies()
        numTrophies = 0
        for i in xrange(30):
            if avTrophies[i] != 0:
                numTrophies += 1
        oldLaffBoost = int(numTrophies/10)
        genre = RaceGlobals.getTrackGenre(self.trackId)
        trophies = []
        if won:
            kartingHistory[genre] += 1
            kartingHistory[3] += 1
            if kartingHistory[3] > RaceGlobals.TotalWonRaces:
                avTrophies[RaceGlobals.TotalWins] = 1
                trophies.append(RaceGlobals.TotalWins)
            for i in xrange(3):
                if kartingHistory[genre] >= RaceGlobals.WonRaces[i] and avTrophies[RaceGlobals.AllWinsList[genre][i]] != 1:
                    avTrophies[RaceGlobals.AllWinsList[genre][i]] = 1
                    trophies.append(RaceGlobals.AllWinsList[genre][i])
        if qualify:
            kartingHistory[genre + 4] += 1
            kartingHistory[7] += 1
            if kartingHistory[7] >= RaceGlobals.TotalQualifiedRaces and avTrophies[RaceGlobals.TotalQuals] != 1:
                avTrophies[RaceGlobals.TotalQuals] = 1
                trophies.append(RaceGlobals.TotalQuals)
            for i in xrange(3):
                if kartingHistory[genre + 4] >= RaceGlobals.QualifiedRaces[i] and avTrophies[RaceGlobals.AllQualsList[genre][i]] != 1:
                    avTrophies[RaceGlobals.AllQualsList[genre][i]] = 1
                    trophies.append(RaceGlobals.AllQualsList[genre][i])
        for i, history in enumerate(kartingHistory):
            if history > 255:
                kartingHistory[i] = 255
        av.b_setKartingHistory(kartingHistory)
        pKartingBest = av.getKartingPersonalBestAll()
        trackIndex = TTLocalizer.KartRace_TrackNames.keys().index(self.trackId)
        if pKartingBest[trackIndex] > time or not pKartingBest[trackIndex]:
            pKartingBest[trackIndex] = time
            av.b_setKartingPersonalBest(pKartingBest)
        gTourTrophy = True
        for bestTime in pKartingBest:
            if not bestTime:
                gTourTrophy = False
        if gTourTrophy:
            if avTrophies[RaceGlobals.GrandTouring] != 1:
                avTrophies[RaceGlobals.GrandTouring] = 1
                trophies.append(RaceGlobals.GrandTouring)
        newLaffBoost = int((len(trophies) + numTrophies)/10)
        if newLaffBoost - oldLaffBoost != 0:
            for i in xrange(newLaffBoost):
                if avTrophies[RaceGlobals.TrophyCups[i]] != 1:
                    avTrophies[RaceGlobals.TrophyCups[i]] = 1
                    trophies.append(RaceGlobals.TrophyCups[i])
            av.b_setMaxHp(av.getMaxHp() + newLaffBoost - oldLaffBoost)
            av.toonUp(av.getMaxHp())
        av.b_setKartingTrophies(avTrophies)
        return trophies

    def requestThrow(self, x, y, z):
        avId = self.air.getAvatarIdFromSender()
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to throw a gag in a race they\'re not in!')
        if self.avatarGags[avId] == RaceGlobals.BANANA:
            gag = DistributedGagAI(self.air)
            gag.setRace(self.doId)
            gag.setOwnerId(avId)
            gag.setPos(x, y, z)
            gag.setType(0)
            gag.setInitTime(globalClockDelta.getRealNetworkTime())
            gag.setActivateTime(globalClockDelta.getRealNetworkTime())
            gag.generateWithRequired(self.zoneId)
            self.livingGags.append(gag)
        elif self.avatarGags[avId] == RaceGlobals.TURBO:
            pass
        elif self.avatarGags[avId] == RaceGlobals.ANVIL:
            places = sorted(self.avatarProgress, key=self.avatarProgress.get, reverse=True)
            for i in places:
                if not i in self.finishedAvatars and not self.currentlyAffectedByAnvil[i]:
                    currAvatar = i
                    break
            self.currentlyAffectedByAnvil[avId] = True
            taskMgr.doMethodLater(RaceGlobals.AnvilSquishDuration, self.unsquish, 'unsquish-%i' % currAvatar, [currAvatar])
            self.sendUpdate('dropAnvilOn', [avId, currAvatar, globalClockDelta.getRealNetworkTime()])
        elif self.avatarGags[avId] == RaceGlobals.PIE:
            places = sorted(self.avatarProgress, key=self.avatarProgress.get)
            avPlace = places.index(avId)
            if avPlace + 1 == len(places):
                target = 0
            else:
                target = places[avPlace + 1]
            self.sendUpdate('shootPiejectile', [avId, target, 0])
        else:
            self.air.writeServerEvent('suspicious', avId, 'Toon use race gag while not having one!')
        self.avatarGags[avId] = 0

    def unsquish(self, avId):
        self.currentlyAffectedByAnvil[avId] = False

    def playerLeave(self, avId):
        self.sendUpdate('racerDisconnected', [avId])
        if avId in self.avatars:
            self.avatars.remove(avId)
        count = 0
        for aK in self.avatarKarts:
            if aK[0] == avId and aK[1] in self.air.doId2do:
                self.air.doId2do[aK[1]].handleUnexpectedExit()
                del self.avatarKarts[count]
                break
            count += 1
        if len(self.avatars) == 0:
            self.requestDelete()

    def requestKart(self):
        pass
        avId = self.air.getAvatarIdFromSender()
        accId = self.air.getAccountIdFromSender()
        if not avId in self.avatars:
            self.air.writeServerEvent('suspicious', avId, 'Toon tried to request kart in race they\'re not in!')
            return
        for aK in self.avatarKarts:
            if aK[0] == avId:
                self.air.doId2do[aK[1]].request('Controlled', avId, accId)
                self.air.doId2do[aK[1]].sendUpdate('setInput', [0])