from direct.distributed.DistributedNodeAI import DistributedNodeAI
from direct.distributed.ClockDelta import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from direct.fsm import StateData
from direct.distributed.ClockDelta import *
from direct.interval.IntervalGlobal import *

class DistributedFindFourAI(DistributedNodeAI):

    def __init__(self, air, parent, name, x, y, z, h, p, r):
        DistributedNodeAI.__init__(self, air)
        self.name = name
        self.air = air
        self.setPos(x, y, z)
        self.setHpr(h, p, r)
        self.myPos = (
         x, y, z)
        self.myHpr = (h, p, r)
        self.board = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]]
        self.parent = self.air.doId2do[parent]
        self.parentDo = parent
        self.wantStart = []
        self.playersPlaying = []
        self.playersSitting = 0
        self.playersTurn = 1
        self.movesMade = 0
        self.playerNum = 1
        self.winDirection = None
        self.playersGamePos = [
         None, None]
        self.wantTimer = True
        self.timerEnd = 0
        self.turnEnd = 0
        self.playersObserving = []
        self.winLaffPoints = 20
        self.movesRequiredToWin = 10
        self.zoneId = self.air.allocateZone()
        self.generateOtpObject(air.districtId, self.zoneId, optionalFields=['setX', 'setY', 'setZ', 'setH', 'setP', 'setR'])
        self.parent.setCheckersZoneId(self.zoneId)
        self.timerStart = None
        self.fsm = ClassicFSM.ClassicFSM('Checkers', [
         State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [
          'playing']),
         State.State('playing', self.enterPlaying, self.exitPlaying, [
          'gameOver']),
         State.State('gameOver', self.enterGameOver, self.exitGameOver, [
          'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin')
        self.fsm.enterInitialState()
        return

    def announceGenerate(self):
        self.parent.setGameDoId(self.doId)

    def getTableDoId(self):
        return self.parentDo

    def delete(self):
        self.fsm.requestFinalState()
        self.parent = None
        self.parentDo = None
        del self.board
        del self.fsm
        DistributedNodeAI.delete(self)
        return

    def informGameOfPlayer(self):
        self.playersSitting += 1
        if self.playersSitting < 2:
            self.timerEnd = 0
        else:
            if self.playersSitting == 2:
                self.timerEnd = globalClock.getRealTime() + 20
                self.parent.isAccepting = False
                self.parent.sendUpdate('setIsPlaying', [1])
            else:
                if self.playersSitting > 2:
                    pass
        self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)])

    def informGameOfPlayerLeave(self):
        self.playersSitting -= 1
        if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.timerEnd = 0
            self.parent.isAccepting = True
            self.parent.sendUpdate('setIsPlaying', [0])
        if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin':
            pass
        else:
            self.timerEnd = 0
        if self.timerEnd != 0:
            self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)])
        else:
            self.sendUpdate('setTimer', [0])

    def setGameCountdownTime(self):
        self.timerEnd = globalClock.getRealTime() + 10

    def setTurnCountdownTime(self):
        self.turnEnd = globalClock.getRealTime() + 25

    def getTimer(self):
        if self.timerEnd != 0:
            return 0
        else:
            return 0

    def getTurnTimer(self):
        return globalClockDelta.localToNetworkTime(self.turnEnd)

    def requestTimer(self):
        avId = self.air.getAvatarIdFromSender()
        self.sendUpdateToAvatarId(avId, 'setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)])

    def handlePlayerExit(self, avId):
        if avId in self.wantStart:
            self.wantStart.remove(avId)
        if self.fsm.getCurrentState().getName() == 'playing':
            gamePos = self.playersGamePos.index(avId)
            self.playersGamePos[gamePos] = None
            self.fsm.request('gameOver')
        return

    def handleEmptyGame(self):
        self.movesMade = 0
        self.playersPlaying = []
        self.playersTurn = 1
        self.playerNum = 1
        self.fsm.request('waitingToBegin')
        self.parent.isAccepting = True

    def requestWin(self, pieceNum):
        avId = self.air.getAvatarIdFromSender()
        playerNum = self.playersGamePos.index(avId) + 1
        x = pieceNum[0]
        y = pieceNum[1]
        if self.checkWin(x, y, playerNum) == True:
            self.sendUpdate('announceWinnerPosition', [x, y, self.winDirection, playerNum])
            winnersSequence = Sequence(Wait(5.0), Func(self.fsm.request, 'gameOver'), Func(self.parent.announceWinner, 'Find Four', avId))
            winnersSequence.start()
        else:
            self.sendUpdateToAvatarId(avId, 'illegalMove', [])

    def distributeLaffPoints(self):
        for x in self.parent.seats:
            if x != None:
                av = self.air.doId2do.get(x)
                av.toonUp(self.winLaffPoints)

        return

    def enterWaitingToBegin(self):
        self.setGameCountdownTime()
        self.parent.isAccepting = True

    def exitWaitingToBegin(self):
        self.turnEnd = 0

    def enterPlaying(self):
        self.parent.isAccepting = False
        for x in self.playersGamePos:
            if x != None:
                self.playersTurn = self.playersGamePos.index(x)
                self.d_sendTurn(self.playersTurn + 1)
                break

        self.setTurnCountdownTime()
        self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)])
        return

    def exitPlaying(self):
        pass

    def enterGameOver(self):
        self.timerEnd = 0
        isAccepting = True
        self.parent.handleGameOver()
        self.playersObserving = []
        self.playersTurn = 1
        self.playerNum = 1
        self.playersPlaying = []
        self.movesMade = 0
        self.playersGamePos = [None, None]
        self.parent.isAccepting = True
        self.fsm.request('waitingToBegin')
        return

    def exitGameOver(self):
        pass

    def requestBegin(self):
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.wantStart:
            self.wantStart.append(avId)
        numPlayers = 0
        for x in self.parent.seats:
            if x != None:
                numPlayers = numPlayers + 1

        if len(self.wantStart) == numPlayers and numPlayers >= 2:
            self.d_gameStart(avId)
            self.parent.sendIsPlaying()
        return

    def d_gameStart(self, avId):
        for x in self.playersObserving:
            self.sendUpdateToAvatarId(x, 'gameStart', [255])

        zz = 0
        numPlayers = 0
        for x in self.parent.seats:
            if x != None:
                numPlayers += 1
                self.playersPlaying.append(x)

        if numPlayers == 2:
            player1 = self.playersPlaying[0]
            self.sendUpdateToAvatarId(player1, 'gameStart', [1])
            self.playersGamePos[0] = player1
            player2 = self.playersPlaying[1]
            self.sendUpdateToAvatarId(player2, 'gameStart', [2])
            self.playersGamePos[1] = player2
        self.wantStart = []
        self.fsm.request('playing')
        self.parent.getTableState()
        return

    def d_sendTurn(self, playersTurn):
        self.sendUpdate('sendTurn', [playersTurn])

    def advancePlayerTurn(self):
        if self.playersTurn == 0:
            self.playersTurn = 1
            self.playerNum = 2
        else:
            self.playerNum = 1
            self.playersTurn = 0

    def requestMove(self, moveColumn):
        avId = self.air.getAvatarIdFromSender()
        turn = self.playersTurn
        if avId in self.playersGamePos:
            if self.playersGamePos.index(avId) != self.playersTurn:
                pass
        if self.board[0][moveColumn] != 0:
            self.sendUpdateToAvatarId(avId, 'illegalMove', [])
        for x in range(6):
            if self.board[x][moveColumn] == 0:
                movePos = x

        self.board[movePos][moveColumn] = self.playersTurn + 1
        if self.checkForTie() == True:
            self.sendUpdate('setGameState', [self.board, moveColumn, movePos, turn])
            self.sendUpdate('tie', [])
            winnersSequence = Sequence(Wait(8.0), Func(self.fsm.request, 'gameOver'))
            winnersSequence.start()
            return
        self.movesMade += 1
        self.advancePlayerTurn()
        self.setTurnCountdownTime()
        self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)])
        self.d_sendTurn(self.playersTurn + 1)
        self.sendUpdate('setGameState', [self.board, moveColumn, movePos, turn])

    def checkForTie(self):
        for x in range(7):
            if self.board[0][x] == 0:
                return False

        return True

    def getState(self):
        return self.fsm.getCurrentState().getName()

    def getName(self):
        return self.name

    def getGameState(self):
        return [
         self.board, 0, 0, 0]

    def clearBoard(self):
        for x in self.board.squareList:
            x.setState(0)

    def getPosHpr(self):
        return self.posHpr

    def tempSetBoardState(self):
        self.board = [
         [
          0, 0, 0, 0, 0, 0, 0], [1, 2, 1, 2, 2, 2, 1], [2, 2, 1, 2, 1, 2, 1], [2, 1, 1, 2, 2, 1, 2], [1, 2, 2, 1, 2, 1, 1], [1, 2, 1, 2, 1, 2, 1]]
        self.sendUpdate('setGameState', [self.board, 0, 0, 1])

    def checkWin(self, rVal, cVal, playerNum):
        if self.checkHorizontal(rVal, cVal, playerNum) == True:
            self.winDirection = 0
            return True
        elif self.checkVertical(rVal, cVal, playerNum) == True:
            self.winDirection = 1
            return True
        elif self.checkDiagonal(rVal, cVal, playerNum) == True:
            self.winDirection = 2
            return True
        else:
            self.winDirection = None
            return False
        return

    def checkHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 2:
            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 4:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            for x in range(1, 4):
                if self.board[rVal + x][cVal] != playerNum:
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 3:
            for x in range(1, 4):
                if self.board[rVal - x][cVal] != playerNum:
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkDiagonal(self, rVal, cVal, playerNum):
        if cVal <= 2:
            if rVal == 2:
                for x in range(1, 4):
                    if self.board[rVal + x][cVal + x] != playerNum:
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in range(1, 4):
                    if self.board[rVal - x][cVal + x] != playerNum:
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return True

                return False
        elif cVal >= 4:
            if rVal == 2:
                for x in range(1, 4):
                    if self.board[rVal + x][cVal - x] != playerNum:
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in range(1, 4):
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True

                return False
        elif rVal == 3 or rVal == 4 or rVal == 5:
            for x in range(1, 4):
                if self.board[rVal - x][cVal - x] != playerNum:
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal + x][cVal - x] != playerNum:
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 0 or rVal == 1 or rVal == 2:
            for x in range(1, 4):
                if self.board[rVal + x][cVal - x] != playerNum:
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal + x][cVal + x] != playerNum:
                    break
                if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                    return True

            return False
        return False