from src.basic import *
from src.prefab.soldier import *

# > sort 11 


class SnakeBodyType(CreepLogic):
    NAME = "SnakeBodyType"

    def onStep(self, c: Creep, k: GlobalKnowledge, *refs) -> None:
        c.autoCombat()

        if not self.team:
            ec = c.nearest(k.battles.enemies, 25)
            if ec: c.escape(ec)


class __SnakeHeadII(SnakeBodyType):
    NAME = "__SnakeHeadII"
    RECIPE = "M15A35"


class __SnakeHealerII(SnakeBodyType):
    NAME = "__SnakeHealerII"
    RECIPE = "M35H10"


class __SnakeHeadIII(SnakeBodyType):
    NAME = "__SnakeHeadIII"
    RECIPE = "M1A40T9"


class __SnakeHealerIII(SnakeBodyType):
    NAME = "__SnakeHealerIII"
    RECIPE = "M30H20"


class __SnakeMoverIII(CreepLogic):
    NAME = "__SnakeMoverIII"
    RECIPE = "M50"


class SnakeTeamTypeII(TeamLogic):
    NAME = "SnakeTeamTypeII"
    CREATE = True
    SEARCH = True
    motionOption = DEFAULT_MOTION
    HPSET = {
        "high": 0.9,
        "low": 0.8
    }
    RALLY = {
        "point": None,
        "distance": 2
    }
    RANGE = {
        "vision": 50,
        "combat": 10
    }
    WALL = False
    PATROL = [
        SPAWN.offset(SIDE * 3, SIDE * 3),
        SPAWN.offset(SIDE * 3, SIDE * -3),
        SPAWN.offset(SIDE * -3, SIDE * -3),
        SPAWN.offset(SIDE * -3, SIDE * 3),
    ]

    a = Mate("__SnakeHeadII", SPAWN)
    h = Mate("__SnakeHealerII", SPAWN)

    def onLoading(self, it: any, k: GlobalKnowledge, *children) -> None:
        SoldierType.onLoading(self, it, k, *children)

    @property
    def hp(self) -> int:
        if not self.a or not self.h:
            return 0
        return self.a.hp + self.h.hp

    @property
    def hpMax(self) -> int:
        if not self.a or not self.h:
            return 0
        return self.a.hpMax + self.h.hpMax

    @property
    def hpPer(self) -> float:
        if not self.a or not self.h:
            return 0
        return math.min(self.a.hpPer, self.h.hpPer)

    def _onRallyArea(self, a: Creep, h: Creep) -> bool | None:
        """
        集结区逻辑。
        可以返回True，那么将转到行军状态
        也可以返回False，会继续保持集结状态
        """
        return True

    def rally(self):
        if self.a:
            if self.__class__.RALLY and self.__class__.RALLY["point"]:
                self.a.follow(self.__class__.RALLY["point"], self.__class__.RALLY["distance"], self.motionOption)
        if self.h:
            if self.__class__.RALLY and self.__class__.RALLY["point"]:
                self.h.follow(self.__class__.RALLY["point"], self.__class__.RALLY["distance"], self.motionOption)
        if self.a and self.h and self._onRallyArea(self.a, self.h):
            return "march"

    def __selectTarget(self, a: Creep, h: Creep):
        e = a.nearest(k.battles.enemies, self.RANGE["vision"])
        if e is not None:
            return self._onTargetSelect(a, h, e)
        if self.target:
            return self._onTargetSelect(a, h, self.target)
        e = a.nearest(k.enemies, self.RANGE["vision"])
        if e is not None:
            return self._onTargetSelect(a, h, e)
        ext = a.nearest(get.extensions(st.enemy), self.RANGE["vision"])
        if ext is not None:
            return self._onTargetSelect(a, h, ext)
        sp = a.closest(get.spawns(st.enemy))
        if sp is not None:
            return self._onTargetSelect(a, h, sp)
        return self._onTargetSelect(a, h, None)

    def _onTargetSelect(self, a: Creep, h: Creep, target: Creep | Structure | None) -> Creep | Structure | Point | None:
        """
        选择最终的目标。
        可以返回Creep|Structure，这将确信触发战斗
        也可以返回Point，用于移动到指定位置。
        返回None时，执行巡逻。

        Args:
            c (Creep):  creep
            target (Creep | Structure | None): 目标

        Returns:
            Creep | Structure | Point | None: 最终的目标
        """
        return target

    def _onEnterCombatArea(self, a: Creep, h: Creep, target: Creep | Structure) -> bool | None:
        """
        进入交战距离时触发。
        可以返回True，进入交战状态
        也可以返回False，这将保持当前状态，并与敌人保持距离
        """
        return True

    def _onCombatArea(self, a: Creep, h: Creep, target: Creep | Structure) -> bool | None:
        """
        正处于交战区域中。
        可以返回True，继续执行交战逻辑。
        也可以返回False，阻止执行交战逻辑，这会强迫目标从交战区域中撤退。
        """
        return True

    def march(self):
        if not self.a or not self.h:
            self.alive = False
            return
        e = self.__selectTarget(self.a, self.h)
        if st.na(e):
            self.a.patrol(self.PATROL, self.motionOption)
            self.h.push(self.a)
            return
        self.a.move(e, self.motionOption)
        self.h.push(self.a)
        if (st.creep(e) or st.structure(e)) and self.a.near(e, self.RANGE["combat"]):
            if self._onEnterCombatArea(self.a, self.h, e):
                return "combat"
            self.h.escape(e, self.motionOption)
            self.h.pull(self.a)

    def combat(self):
        if not self.a or not self.h:
            self.alive = False
            return
        e = self.__selectTarget(self.a, self.h)
        if e is None or self.a.distance(e) > self.RANGE["combat"]:
            return "march"
        if not self._onCombatArea(self.a, self.h, e):
            self.h.escape(e, self.motionOption)
            self.h.pull(self.a)
            return "march"
        bdist = SoldierType.__measureDist(self, self.a, e, False)
        
        res = self.a.test(e, bdist)
        # print(f"{self.name}:bdist = {bdist}, res = {res}")

        match res:
            case True:
                self.a.move(e, self.motionOption)
                self.h.push(self.a)
            case False:
                self.h.escape(e, self.motionOption)
                self.h.pull(self.a)


class SnakerTeamTypeIII(TeamLogic):
    NAME = "SnakerTeamTypeIII"
    CREATE = True
    SEARCH = True
    motionOption = DEFAULT_MOTION
    HPSET = {
        "high": 0.9,
        "low": 0.8
    }
    RALLY = {
        "point": None,
        "distance": 2
    }
    RANGE = {
        "vision": 50,
        "combat": 10
    }
    WALL = False
    PATROL = [
        SPAWN.offset(SIDE * 3, SIDE * 3),
        SPAWN.offset(SIDE * 3, SIDE * -3),
        SPAWN.offset(SIDE * -3, SIDE * -3),
        SPAWN.offset(SIDE * -3, SIDE * 3),
    ]

    a = Mate("__SnakeHeadIII", SPAWN)
    h = Mate("__SnakeHealerIII", SPAWN)
    m = Mate("__SnakeMoverIII", SPAWN)

    def onLoading(self, it: any, k: GlobalKnowledge, *children) -> None:
        SoldierType.onLoading(self, it, k, *children)

    @property
    def hp(self) -> int:
        if not self.a or not self.h or not self.m:
            return 0
        return self.a.hp + self.h.hp + self.m.hp

    @property
    def hpMax(self) -> int:
        if not self.a or not self.h or not self.m:
            return 0
        return self.a.hpMax + self.h.hpMax + self.m.hpMax

    @property
    def hpPer(self) -> float:
        if not self.a or not self.h or not self.m:
            return 0
        return math.min(self.a.hpPer, self.h.hpPer, self.m.hpPer)

    def _onRallyArea(self, a: Creep, h: Creep, m: Creep) -> bool | None:
        """
        集结区逻辑。
        可以返回True,那么将转到行军状态
        也可以返回False,会继续保持集结状态
        """
        return True

    def rally(self):
        if self.a:
            if self.__class__.RALLY and self.__class__.RALLY["point"]:
                self.a.follow(self.__class__.RALLY["point"], self.__class__.RALLY["distance"], self.motionOption)
        if self.h:
            if self.__class__.RALLY and self.__class__.RALLY["point"]:
                self.h.follow(self.__class__.RALLY["point"], self.__class__.RALLY["distance"], self.motionOption)
        if self.m:
            if self.__class__.RALLY and self.__class__.RALLY["point"]:
                self.m.follow(self.__class__.RALLY["point"], self.__class__.RALLY["distance"], self.motionOption)
        if self.a and self.h and self.m and self._onRallyArea(self.a, self.h, self.m):
            return "march"

    def __selectTarget(self, a: Creep, h: Creep, m: Creep):
        e = a.nearest(k.battles.enemies, self.RANGE["vision"])
        if e is not None:
            return self._onTargetSelect(a, h, m, e)
        if self.target:
            return self._onTargetSelect(a, h, m, self.target)
        e = a.nearest(k.enemies, self.RANGE["vision"])
        if e is not None:
            return self._onTargetSelect(a, h, m, e)
        ext = a.nearest(get.extensions(st.enemy), self.RANGE["vision"])
        if ext is not None:
            return self._onTargetSelect(a, h, m, ext)
        sp = a.closest(get.spawns(st.enemy))
        if sp is not None:
            return self._onTargetSelect(a, h, m, sp)
        return self._onTargetSelect(a, h, m, None)

    def _onTargetSelect(self, a: Creep, h: Creep, m: Creep, target: Creep | Structure | None) -> Creep | Structure | Point | None:
        """
        选择最终的目标。
        可以返回Creep|Structure,这将确信触发战斗
        也可以返回Point,用于移动到指定位置。
        返回None时,执行巡逻。
        """
        return target

    def _onEnterCombatArea(self, a: Creep, h: Creep, m: Creep, target: Creep | Structure) -> bool | None:
        """
        进入交战距离时触发。
        可以返回True,进入交战状态
        也可以返回False,这将保持当前状态,并与敌人保持距离
        """
        return True

    def _onCombatArea(self, a: Creep, h: Creep, m: Creep, target: Creep | Structure) -> bool | None:
        """
        正处于交战区域中。
        可以返回True,继续执行交战逻辑。
        也可以返回False,阻止执行交战逻辑,这会强迫目标从交战区域中撤退。
        """
        return True

    def march(self):
        if not self.a or not self.h or not self.m:
            self.alive = False
            return
        e = self.__selectTarget(self.a, self.h, self.m)
        if st.na(e):
            self.a.patrol(self.PATROL, self.motionOption)
            self.h.push(self.a)
            self.m.push(self.h)
            print(f"[{self.NAME}.march:patrol] a=({self.a.x},{self.a.y}) h=({self.h.x},{self.h.y}) m=({self.m.x},{self.m.y}) d(h,a)={self.h.distance(self.a)} d(m,h)={self.m.distance(self.h)}")
            return
        self.a.move(e, self.motionOption)
        self.h.push(self.a)
        self.m.push(self.h)
        print(f"[{self.NAME}.march:move] e=({e.x},{e.y}) a=({self.a.x},{self.a.y}) h=({self.h.x},{self.h.y}) m=({self.m.x},{self.m.y}) d(h,a)={self.h.distance(self.a)} d(m,h)={self.m.distance(self.h)}")
        if (st.creep(e) or st.structure(e)) and self.a.near(e, self.RANGE["combat"]):
            if self._onEnterCombatArea(self.a, self.h, self.m, e):
                return "combat"
            self.m.escape(e, self.motionOption)
            self.m.pull(self.h)
            self.h.pull(self.a)

    def combat(self):
        if not self.a or not self.h or not self.m:
            self.alive = False
            return
        e = self.__selectTarget(self.a, self.h, self.m)
        if e is None or self.a.distance(e) > self.RANGE["combat"]:
            return "march"
        if not self._onCombatArea(self.a, self.h, self.m, e):
            self.m.escape(e, self.motionOption)
            self.m.pull(self.h)
            self.h.pull(self.a)
            return "march"
        bdist = SoldierType.__measureDist(self, self.a, e, False)

        res = self.a.test(e, bdist)
        print(f"[{self.NAME}.combat] e=({e.x},{e.y}) bdist={bdist} res={res} a=({self.a.x},{self.a.y}) h=({self.h.x},{self.h.y}) m=({self.m.x},{self.m.y}) d(h,a)={self.h.distance(self.a)} d(m,h)={self.m.distance(self.h)}")

        match res:
            case True:
                self.a.move(e, self.motionOption)
                self.h.push(self.a)
                self.m.push(self.h)
            case False:
                self.m.escape(e, self.motionOption)
                self.m.pull(self.h)
                self.h.pull(self.a)


"""$TEMPLATE
from src.prefab import *


class SnakeHead(SnakeBodyType):
    NAME = "SnakeHead"
    RECIPE = "M12A35T3"


class SnakeHealer(SnakeBodyType):
    NAME = "SnakeHealer"
    RECIPE = "M40H10"


class SnakeII(SnakeTeamTypeII):
    NAME = "SnakeII"
    
    a = Mate("SnakeHead", SPAWN)
    h = Mate("SnakeHealer", SPAWN)

    def onLoading(self, it: any, k: GlobalKnowledge, *refs) -> None:
        super().onLoading(it, k, *refs)

    def _onTargetSelect(self, a: Creep, h: Creep, target: Creep | Structure | None) -> Creep | Structure | Point | None:
        return target

"""
