from scipy.spatial import distance
from src.gameplay.typings import Context
import src.gameplay.utils as gameplayUtils
from src.gameplay.targeting import getFirstCreatureOnPath
import src.utils.mouse as mouse
from ...typings import Context
from ...utils import releaseKeys
from ..waypoint import generateFloorWalkpoints
from .common.vector import VectorTask
from .walk import WalkTask

class WalkToTargetCreatureTask(VectorTask):
    def __init__(self):
        super().__init__()
        self.name = 'walkToTargetCreature'
        self.manuallyTerminable = True
        self.targetCreatureCoordinateSinceLastRestart = None

    def onBeforeStart(self, context: Context) -> Context:
        self.calculatePathToTargetCreature(context)
        return context

    def onBeforeRestart(self, context: Context) -> Context:
        context = releaseKeys(context)
        return self.onBeforeStart(context)

    def onInterrupt(self, context: Context) -> Context:
        return releaseKeys(context)

    def onComplete(self, context: Context) -> Context:
        return releaseKeys(context)

    # TODO: if there are no more creatures, it should only recalculate when it gets close to the creature to avoid recalculating each SQM move
    def shouldRestart(self, context: Context) -> bool:
        if context['ng_cave']['targetCreature'] is None:
            return False
        return not gameplayUtils.coordinatesAreEqual(context['ng_cave']['targetCreature']['coordinate'], self.targetCreatureCoordinateSinceLastRestart)

    def shouldManuallyComplete(self, context: Context) -> bool:
        if context['ng_cave']['isAttackingSomeCreature'] == False:
            return True
        return False

    def calculatePathToTargetCreature(self, context: Context):
        self.tasks = []
        if context['ng_cave']['targetCreature'] is None:
            return
        originalTarget = context['ng_cave']['targetCreature']
        originalCoordinate = originalTarget['coordinate']
        openPath = generateFloorWalkpoints(
            context['ng_radar']['coordinate'],
            originalCoordinate,
            nonWalkableCoordinates=context['ng_cave']['holesOrStairs'].copy(),
        )
        blocker = getFirstCreatureOnPath(
            context['gameWindow']['monsters'],
            openPath,
            excludedCoordinate=originalCoordinate,
        )
        target = blocker or originalTarget
        targetCoordinate = target['coordinate']
        if blocker is not None and blocker.get('windowCoordinate') is not None:
            mouse.attackClick(blocker['windowCoordinate'])

        nonWalkableCoordinates = context['ng_cave']['holesOrStairs'].copy()
        for monster in context['gameWindow']['monsters']:
            coordinate = monster.get('coordinate')
            if coordinate is not None and not gameplayUtils.coordinatesAreEqual(
                    coordinate, targetCoordinate):
                nonWalkableCoordinates.append(coordinate)
        walkpoints = []
        dist = distance.cdist([context['ng_radar']['coordinate']], [
                            targetCoordinate]).flatten()[0]
        if dist < 2:
            gameWindowHeight, gameWindowWidth = context['gameWindow']['image'].shape
            gameWindowCenter = (gameWindowWidth // 2, gameWindowHeight // 2)
            monsterGameWindowCoordinate = target['gameWindowCoordinate']
            moduleX = abs(gameWindowCenter[0] - monsterGameWindowCoordinate[0])
            moduleY = abs(gameWindowCenter[1] - monsterGameWindowCoordinate[1])
            if moduleX > 64 or moduleY > 64:
                walkpoints.append(targetCoordinate)
        else:
            walkpoints = generateFloorWalkpoints(
                context['ng_radar']['coordinate'], targetCoordinate,
                nonWalkableCoordinates=nonWalkableCoordinates)
            if walkpoints:
                walkpoints.pop()
        for walkpoint in walkpoints:
            self.tasks.append(WalkTask(context, walkpoint).setParentTask(
                self).setRootTask(self.rootTask))
        self.targetCreatureCoordinateSinceLastRestart = list(originalCoordinate)
