from .DistributedMinigameAI import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from toontown.minigame import PlayingCardGlobals
from toontown.minigame import PlayingCard
from toontown.minigame.PlayingCard import PlayingCardBase
from toontown.minigame import PlayingCardDeck
from toontown.minigame import PairingGameGlobals
from toontown.ai.ToonBarrier import ToonBarrier

class DistributedPairingGameAI(DistributedMinigameAI):
    notify = directNotify.newCategory('DistributedPairingGameAI')
    OneCardInMultiplayer = True
    TurnDownTwoAtATime = True

    def __init__(self, air, minigameId):
        try:
            self.DistributedPairingGameAI_initialized
        except:
            self.DistributedPairingGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedPairingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
            self.addChildGameFSM(self.gameFSM)
            self.gameFSM.enterInitialState()
            self.deckSeed = random.randint(0, 4000000)
            self.faceUpDict = {}
            self.inactiveList = []
            self.maxOpenCards = 2
            self.points = 0
            self.flips = 0
            self.matches = 0
            self.cards = []
            self.gameDuration = 90

    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setGameReady(self):
        self.notify.debug('setGameReady')
        if self.OneCardInMultiplayer and len(self.avIdList) > 1:
            self.maxOpenCards = 1
        self.sendUpdate('setMaxOpenCards', [self.maxOpenCards])
        DistributedMinigameAI.setGameReady(self)
        for avId in self.avIdList:
            self.faceUpDict[avId] = []

        self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers)
        for index in range(len(self.deck.cards)):
            cardValue = self.deck.cards[index]
            oneCard = PlayingCardBase(cardValue)
            self.cards.append(oneCard)

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def calcLowFlipBonus(self):
        lowFlipModifier = PairingGameGlobals.calcLowFlipModifier(self.matches, self.flips)
        bonus = lowFlipModifier * self.matches
        self.notify.debug('low flip bonus = %d' % bonus)
        return bonus

    def gameOver(self):
        self.notify.debug('gameOver')
        lowFlipBonus = 0
        for avId in self.avIdList:
            self.scoreDict[avId] = max(1, self.points)
            lowFlipBonus = self.calcLowFlipBonus()
            self.scoreDict[avId] += lowFlipBonus
            if self.matches == len(self.cards) / 2:
                self.scoreDict[avId] += round(len(self.cards) / 4.0)
                self.logAllPerfect()

        logAvId = self.avIdList[0]
        self.air.writeServerEvent('minigame_pairing', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.PairingGameId,
         self.getSafezoneId(),
         self.avIdList,
         self.scoreDict[logAvId],
         self.gameDuration,
         self.matches,
         self.flips,
         lowFlipBonus))
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    def exitInactive(self):
        pass

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

        def allToonsDone(self = self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not PairingGameGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self = self):
            self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.gameDuration = PairingGameGlobals.calcGameDuration(self.getDifficulty())
        self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, self.gameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)

    def exitPlay(self):
        self.doneBarrier.cleanup()
        del self.doneBarrier

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass

    def getDeckSeed(self):
        return self.deckSeed

    def isCardFaceUp(self, deckOrderIndex):
        retval = False
        for key in list(self.faceUpDict.keys()):
            if deckOrderIndex in self.faceUpDict[key]:
                retval = True
                break

        return retval

    def isCardFaceDown(self, deckOrderIndex):
        return not self.isCardFaceUp(deckOrderIndex)

    def checkForMatch(self):
        faceUpList = []
        for oneToonFaceUpList in list(self.faceUpDict.values()):
            faceUpList += oneToonFaceUpList

        for i in range(len(faceUpList)):
            cardA = faceUpList[i]
            for j in range(i + 1, len(faceUpList)):
                cardB = faceUpList[j]
                if self.cards[cardA].rank == self.cards[cardB].rank:
                    return (cardA, cardB)

        return (-1, -1)

    def handleMatch(self, cardA, cardB):
        self.notify.debug('we got a match %d %d' % (cardA, cardB))
        for key in list(self.faceUpDict.keys()):
            if cardA in self.faceUpDict[key]:
                self.faceUpDict[key].remove(cardA)
            if cardB in self.faceUpDict[key]:
                self.faceUpDict[key].remove(cardB)

        self.inactiveList.append(cardA)
        self.inactiveList.append(cardB)

    def turnDownCard(self, cardA):
        self.notify.debug('turning down card %d' % cardA)
        for key in list(self.faceUpDict.keys()):
            if cardA in self.faceUpDict[key]:
                self.faceUpDict[key].remove(cardA)

    def openCardRequest(self, deckOrderIndex, bonusGlowCard):
        if self.isCardFaceUp(deckOrderIndex):
            return
        if self.gameFSM.getCurrentState().getName() != 'play':
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.air.writeServerEvent('suspicious', avId, 'openCardRequest from non-player av %s' % avId)
            return
        if deckOrderIndex < 0 or deckOrderIndex >= len(self.cards):
            self.logSuspicious(avId, 'openCardRequest: invalid deckOrderIndex: %s' % deckOrderIndex)
            return
        if bonusGlowCard < 0 or bonusGlowCard >= len(self.cards):
            self.logSuspicious(avId, 'openCardRequest: invalid bonusGlowCard: %s' % bonusGlowCard)
            return
        cardsToTurnDown = []
        faceUpList = self.faceUpDict[avId]
        numCardsFaceUpAtStart = len(faceUpList)
        if len(faceUpList) >= self.maxOpenCards:
            oldestCard = faceUpList.pop(0)
            cardsToTurnDown.append(oldestCard)
        if self.TurnDownTwoAtATime and numCardsFaceUpAtStart == 2:
            secondOldestCard = faceUpList.pop(0)
            cardsToTurnDown.append(secondOldestCard)
        cardToTurnUp = deckOrderIndex
        self.faceUpDict[avId].append(cardToTurnUp)
        cardA, cardB = self.checkForMatch()
        matchingCard = -1
        if cardA > -1:
            self.handleMatch(cardA, cardB)
            if cardA == deckOrderIndex:
                matchingCard = cardB
            else:
                matchingCard = cardA
            pointsToGive = 1
            if bonusGlowCard in [cardA, cardB]:
                pointsToGive += 1
            self.points += pointsToGive
            self.matches += 1
        self.flips += 1
        self.sendUpdate('openCardResult', [cardToTurnUp,
         avId,
         matchingCard,
         self.points,
         cardsToTurnDown])

    def reportDone(self):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)