from direct.showbase.RandomNumGen import RandomNumGen
from . import CatchGameGlobals
from . import DropScheduler
from toontown.parties.PartyGlobals import CatchActivityDuration as PartyCatchDuration

class DropPlacer:

    def __init__(self, game, numPlayers, dropTypes, startTime = None):
        self.game = game
        self.numPlayers = numPlayers
        self.dropTypes = dropTypes
        self.dtIndex = 0
        self._createScheduler(startTime)
        self._createRng()

    def _createScheduler(self, startTime):
        self.scheduler = DropScheduler.DropScheduler(CatchGameGlobals.GameDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime)

    def _createRng(self):
        self.rng = self.game.randomNumGen

    def skipPercent(self, percent):
        numSkips = self.scheduler.skipPercent(percent)
        self.dtIndex += numSkips
        return numSkips

    def doneDropping(self, continuous = None):
        return self.scheduler.doneDropping(continuous)

    def getDuration(self):
        return self.scheduler.getDuration()

    def getT(self):
        return self.scheduler.getT()

    def stepT(self):
        self.scheduler.stepT()

    def getNextDropTypeName(self):
        if self.dtIndex >= len(self.dropTypes):
            self.game.notify.debug('warning: defaulting to anvil')
            typeName = 'anvil'
        else:
            typeName = self.dropTypes[self.dtIndex]
        self.dtIndex += 1
        return typeName

    def getRandomColRow(self):
        col = self.rng.randrange(0, self.game.DropColumns)
        row = self.rng.randrange(0, self.game.DropRows)
        return [col, row]

    def getNextDrop(self):
        raise RuntimeError('DropPlacer.getNextDrop should never be called')


class RandomDropPlacer(DropPlacer):

    def __init__(self, game, numPlayers, dropTypes, startTime = None):
        DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime)

    def getNextDrop(self):
        col, row = self.getRandomColRow()
        drop = [self.getT(), self.getNextDropTypeName(), [col, row]]
        self.stepT()
        return drop


class RegionDropPlacer(DropPlacer):
    DropRegionTables = [[[1,
       1,
       2,
       3,
       3],
      [1,
       1,
       2,
       3,
       3],
      [0,
       1,
       2,
       3,
       4],
      [0,
       1,
       2,
       3,
       4],
      [0,
       1,
       2,
       3,
       4]],
     [[1,
       2,
       2,
       3,
       3,
       4],
      [1,
       1,
       2,
       3,
       4,
       4],
      [1,
       1,
       2,
       3,
       4,
       4],
      [0,
       1,
       2,
       3,
       4,
       5],
      [0,
       1,
       2,
       3,
       4,
       5],
      [0,
       1,
       2,
       3,
       4,
       5]],
     [[1,
       1,
       2,
       2,
       2,
       3,
       3],
      [1,
       1,
       2,
       2,
       2,
       3,
       3],
      [0,
       1,
       2,
       2,
       2,
       3,
       4],
      [0,
       1,
       2,
       2,
       2,
       3,
       4],
      [0,
       1,
       2,
       2,
       2,
       3,
       4],
      [0,
       1,
       2,
       2,
       2,
       3,
       4],
      [0,
       1,
       2,
       2,
       2,
       3,
       4]],
     [[1,
       2,
       2,
       5,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       6,
       3,
       4,
       4]],
     [[1,
       2,
       2,
       5,
       8,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       8,
       6,
       3,
       4,
       4]],
     [[1,
       2,
       2,
       5,
       8,
       8,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       8,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       8,
       8,
       6,
       3,
       4,
       4]],
     [[1,
       2,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       8,
       10,
       9,
       6,
       3,
       4,
       4]],
     [[1,
       2,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       10,
       9,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       8,
       10,
       10,
       9,
       6,
       3,
       4,
       4]],
     [[1,
       2,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       7,
       3],
      [1,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       3],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       1,
       2,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       7,
       3,
       4],
      [0,
       0,
       1,
       5,
       8,
       10,
       11,
       12,
       9,
       6,
       3,
       4,
       4]]]
    Players2dropTable = [DropRegionTables[0],
     DropRegionTables[0],
     DropRegionTables[0],
     DropRegionTables[1],
     DropRegionTables[1],
     DropRegionTables[2],
     DropRegionTables[3],
     DropRegionTables[3],
     DropRegionTables[4],
     DropRegionTables[4],
     DropRegionTables[5],
     DropRegionTables[5],
     DropRegionTables[5],
     DropRegionTables[6],
     DropRegionTables[6],
     DropRegionTables[7],
     DropRegionTables[7],
     DropRegionTables[7],
     DropRegionTables[8],
     DropRegionTables[8]]

    @classmethod
    def getDropRegionTable(cls, numPlayers):
        return cls.Players2dropTable[min(len(cls.Players2dropTable) - 1, numPlayers)]

    def __init__(self, game, numPlayers, dropTypes, startTime = None):
        DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime)
        self.DropRegionTable = self.getDropRegionTable(self.numPlayers)
        self.DropRegion2GridCoordList = {}
        for row in range(len(self.DropRegionTable)):
            rowList = self.DropRegionTable[row]
            for column in range(len(rowList)):
                region = rowList[column]
                if region not in self.DropRegion2GridCoordList:
                    self.DropRegion2GridCoordList[region] = []
                self.DropRegion2GridCoordList[region].append([row, column])

        self.DropRegions = list(self.DropRegion2GridCoordList.keys())
        self.DropRegions.sort()
        self.emptyDropRegions = self.DropRegions[:]
        self.fallingObjs = []

    def getNextDrop(self):
        t = self.getT()
        while len(self.fallingObjs):
            landTime, dropRegion = self.fallingObjs[0]
            if landTime > t:
                break
            self.fallingObjs = self.fallingObjs[1:]
            if dropRegion not in self.emptyDropRegions:
                self.emptyDropRegions.append(dropRegion)

        candidates = self.emptyDropRegions
        if len(candidates) == 0:
            candidates = self.DropRegions
        dropRegion = self.rng.choice(candidates)
        row, col = self.rng.choice(self.DropRegion2GridCoordList[dropRegion])
        dropTypeName = self.getNextDropTypeName()
        drop = [t, dropTypeName, [row, col]]
        duration = self.game.BaselineDropDuration
        self.fallingObjs.append([t + duration, dropRegion])
        if dropRegion in self.emptyDropRegions:
            self.emptyDropRegions.remove(dropRegion)
        self.stepT()
        return drop


class PartyRegionDropPlacer(RegionDropPlacer):

    def __init__(self, game, numPlayers, generationId, dropTypes, startTime = None):
        self.generationId = generationId
        RegionDropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime)

    def _createRng(self):
        self.rng = RandomNumGen(self.generationId + self.game.doId)

    def _createScheduler(self, startTime):
        self.scheduler = DropScheduler.ThreePhaseDropScheduler(PartyCatchDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.SlowerDropPeriodMult, self.game.NormalDropDelay, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime)


class PathDropPlacer(DropPlacer):

    def __init__(self, game, numPlayers, dropTypes, startTime = None):
        DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime)
        self.moves = [[0, -1],
         [1, -1],
         [1, 0],
         [1, 1],
         [0, 1],
         [-1, 1],
         [-1, 0],
         [-1, -1]]
        self.paths = []
        for i in range(self.numPlayers):
            dir = self.rng.randrange(0, len(self.moves))
            col, row = self.getRandomColRow()
            path = {'direction': dir,
             'location': [col, row]}
            self.paths.append(path)

        self.curPathIndex = 0

    def getValidDirection(self, col, row, dir):
        redirectTop = [(6, 2),
         2,
         2,
         3,
         4,
         5,
         6,
         6]
        redirectRight = [0,
         0,
         (0, 4),
         4,
         4,
         5,
         6,
         7]
        redirectBottom = [0,
         1,
         2,
         2,
         (2, 6),
         6,
         6,
         7]
        redirectLeft = [0,
         1,
         2,
         3,
         4,
         4,
         (4, 0),
         0]
        redirectTopRight = [6,
         (6, 4),
         4,
         4,
         4,
         5,
         6,
         6]
        redirectBottomRight = [0,
         0,
         0,
         (0, 6),
         6,
         6,
         6,
         7]
        redirectBottomLeft = [0,
         1,
         2,
         2,
         2,
         (2, 0),
         0,
         0]
        redirectTopLeft = [2,
         2,
         2,
         3,
         4,
         4,
         4,
         (4, 2)]
        tables = [None,
         redirectTop,
         redirectBottom,
         None,
         redirectLeft,
         redirectTopLeft,
         redirectBottomLeft,
         None,
         redirectRight,
         redirectTopRight,
         redirectBottomRight]
        if col == 0:
            colIndex = 1
        elif col == self.game.DropColumns - 1:
            colIndex = 2
        else:
            colIndex = 0
        if row == 0:
            rowIndex = 1
        elif row == self.game.DropRows - 1:
            rowIndex = 2
        else:
            rowIndex = 0
        index = (colIndex << 2) + rowIndex
        redirectTable = tables[index]
        if not redirectTable:
            return dir
        newDir = redirectTable[dir]
        if type(newDir) != type(1):
            newDir = self.rng.choice(newDir)
        return newDir

    def getNextDrop(self):
        path = self.paths[self.curPathIndex]
        col, row = path['location']
        dir = path['direction']
        turns = [-1,
         0,
         0,
         1]
        turn = self.rng.choice(turns)
        if turn:
            dir = (dir + turn) % len(self.moves)
        dir = self.getValidDirection(col, row, dir)
        dCol, dRow = self.moves[dir]
        col += dCol
        row += dRow
        col = min(max(col, 0), self.game.DropColumns - 1)
        row = min(max(row, 0), self.game.DropRows - 1)
        path['location'] = [col, row]
        path['direction'] = dir
        self.curPathIndex = (self.curPathIndex + 1) % len(self.paths)
        drop = [self.getT(), self.getNextDropTypeName(), [col, row]]
        self.stepT()
        return drop