from CrateGlobals import *
from otp.level import DistributedEntityAI
from direct.directnotify import DirectNotifyGlobal

class DistributedGridAI(DistributedEntityAI.DistributedEntityAI):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGridAI')

    def __init__(self, level, entId):
        self.initialized = 0
        DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId)
        self.activeCellList = []

    def delete(self):
        DistributedEntityAI.DistributedEntityAI.delete(self)
        del self.activeCellList

    def generate(self):
        DistributedEntityAI.DistributedEntityAI.generate(self)

    def initializeGrid(self):
        if not self.initialized:
            self.objPos = {}
            self.gridCells = [
             None] * self.numRow
            for i in range(len(self.gridCells)):
                self.gridCells[i] = [
                 None] * self.numCol
                for j in range(len(self.gridCells[i])):
                    self.gridCells[i][j] = []

            self.initialized = 1
        return

    def addActiveCell(self, cell):
        self.activeCellList.append(cell)

    def getObjPos(self, objId):
        objPos = self.objPos.get(objId, None)
        if objPos:
            row, col = objPos
            if row >= 0 and row < self.numRow and col >= 0 and col < self.numCol:
                return [(col + 1) * self.cellSize, (row + 1) * self.cellSize, 0]
            else:
                self.notify.debug('row/col out of range %s/%s' % (row, col))
        else:
            self.notify.debug("didn't have record of obj")
        return

    def addObjectByPos(self, objId, pos, width=1):
        if not self.initialized:
            self.initializeGrid()
        if self.objPos.get(objId, None):
            return 1
        x, y = pos[0], pos[1]
        col = min(int(x / self.cellSize), self.numCol - width)
        row = min(int(y / self.cellSize), self.numRow - width)
        self.notify.debug('attempt add %d at %s, row,col = %d,%d' % (objId, pos, row, col))
        while col >= 0 and col < self.numCol:
            while row >= 0 and row < self.numRow:
                if self.addObjectByRowCol(objId, row, col):
                    return 1
                else:
                    row += 2
            else:
                row = 0
                col += 2

        else:
            self.notify.debug('requestObjPos: row/col out of range %s/%s' % (row, col))
            row = min(row, self.numRow)
            row = max(0, row)
            col = min(col, self.numRow)
            col = max(0, col)
            return self.addObjectByRowCol(objId, row, col)

        return

    def addObjectByRowCol(self, objId, row, col):
        if row >= 0 and row < self.numRow - 1 and col >= 0 and col < self.numCol - 1:
            self.notify.debug('adding obj %s to grid cell %s,%s' % (objId, row, col))
            self.gridCells[row][col].append(objId)
            self.gridCells[row + 1][col].append(objId)
            self.gridCells[row][col + 1].append(objId)
            self.gridCells[row + 1][col + 1].append(objId)
            self.objPos[objId] = [row, col]
            self.__setChangedActiveCells(onList=[[row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]], objId=objId)
            return 1
        self.notify.debug("couldn't obj to grid cell %s,%s" % (row, col))
        return 0

    def removeObject(self, objId):
        objPos = self.objPos.get(objId)
        if not objPos:
            return
        row, col = objPos
        self.notify.debug('removing obj %s from %s, %s' % (objId, row, col))
        self.gridCells[row][col].remove(objId)
        self.gridCells[row + 1][col].remove(objId)
        self.gridCells[row][col + 1].remove(objId)
        self.gridCells[row + 1][col + 1].remove(objId)
        del self.objPos[objId]
        self.__setChangedActiveCells(offList=[[row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]], objId=objId)

    def checkMoveDir(self, objId, h):
        if h > 225 and h < 315:
            return self.checkMove(objId, 0, 1)
        elif h > 45 and h < 135:
            return self.checkMove(objId, 0, -1)
        elif h < 45 or h > 315:
            return self.checkMove(objId, 1, 0)
        elif h > 135 and h < 225:
            return self.checkMove(objId, -1, 0)

    def doMoveDir(self, objId, h):
        if h > 225 and h < 315:
            return self.doMove(objId, 0, 1)
        elif h > 45 and h < 135:
            return self.doMove(objId, 0, -1)
        elif h < 45 or h > 315:
            return self.doMove(objId, 1, 0)
        elif h > 135 and h < 225:
            return self.doMove(objId, -1, 0)

    def checkPush(self, objId, side):
        if side == 0:
            return self.checkMove(objId, 0, -1)
        else:
            if side == 1:
                return self.checkMove(objId, 0, 1)
            else:
                if side == 2:
                    return self.checkMove(objId, -1, 0)
                else:
                    if side == 3:
                        return self.checkMove(objId, 1, 0)

    def doPush(self, objId, side):
        if side == 0:
            return self.doMove(objId, 0, -1)
        else:
            if side == 1:
                return self.doMove(objId, 0, 1)
            else:
                if side == 2:
                    return self.doMove(objId, -1, 0)
                else:
                    if side == 3:
                        return self.doMove(objId, 1, 0)

    def checkMove(self, objId, dRow, dCol):
        objPos = self.objPos.get(objId)
        if not objPos:
            return
        row, col = objPos
        validMove = 1
        if dRow < 0:
            validMove = validMove & self.__isEmpty(row - 1, col) & self.__isEmpty(row - 1, col + 1)
        else:
            if dRow > 0:
                validMove = validMove & self.__isEmpty(row + 2, col) & self.__isEmpty(row + 2, col + 1)
        if dCol < 0:
            validMove = validMove & self.__isEmpty(row, col - 1) & self.__isEmpty(row + 1, col - 1)
        else:
            if dCol > 0:
                validMove = validMove & self.__isEmpty(row, col + 2) & self.__isEmpty(row + 1, col + 2)
        return validMove

    def doMove(self, objId, dRow, dCol):
        objPos = self.objPos.get(objId)
        if not objPos:
            return 0
        row, col = objPos
        validMove = self.checkMove(objId, dRow, dCol)
        if validMove:
            self.gridCells[row][col].remove(objId)
            self.gridCells[row + 1][col].remove(objId)
            self.gridCells[row][col + 1].remove(objId)
            self.gridCells[row + 1][col + 1].remove(objId)
            newRow = row + dRow
            newCol = col + dCol
            self.gridCells[newRow][newCol].append(objId)
            self.gridCells[newRow + 1][newCol].append(objId)
            self.gridCells[newRow][newCol + 1].append(objId)
            self.gridCells[newRow + 1][newCol + 1].append(objId)
            self.objPos[objId] = [
             newRow, newCol]
            self.updateActiveCells(objId, row, col, dRow, dCol)
        return validMove

    def updateActiveCells(self, objId, row, col, dRow, dCol):
        newRow = row + dRow
        newCol = col + dCol
        newCells = [
         [
          newRow, newCol], [newRow + 1, newCol], [newRow, newCol + 1], [newRow + 1, newCol + 1]]
        oldCells = [
         [
          row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]]
        onList = []
        offList = []
        for cell in newCells:
            if cell not in oldCells:
                onList.append(cell)

        for cell in oldCells:
            if cell not in newCells:
                offList.append(cell)

        self.__setChangedActiveCells(onList, offList, objId)

    def __setChangedActiveCells(self, onList=[], offList=[], objId=None):
        for cell in self.activeCellList:
            self.notify.debug('onList = %s, offList = %s, cell = %s' % (onList, offList, cell.getRowCol()))
            if cell.getRowCol() in onList:
                cell.b_setState(1, objId)
            elif cell.getRowCol() in offList:
                cell.b_setState(0, objId)

    def __isEmpty(self, row, col):
        if row < 0 or row >= self.numRow or col < 0 or col >= self.numCol:
            return 0
        if len(self.gridCells[row][col]) > 0:
            return 0
        return 1

    def printGrid(self):
        if not __debug__:
            return
        for i in range(len(self.gridCells)):
            str = ''
            for j in range(len(self.gridCells[i])):
                col = self.gridCells[i][j]
                active = 0
                for cell in self.activeCellList:
                    if cell.getRowCol() == [i, j]:
                        active = 1

                if len(col) > 0:
                    if active:
                        str += '[X]'
                    else:
                        str += ' X '
                elif active:
                    str += '[.]'
                else:
                    str += ' . '

            print str + '  : %d' % i

        print ''
