from __future__ import annotations
from typing import TYPE_CHECKING
import snecs
from snecs.typedefs import EntityID
from nqp.base_classes.entity_behaviour import EntityBehaviour
from nqp.command.unit import Unit
from nqp.core.constants import HealingSource, PATH_UPDATE_FREQ
from nqp.core.utility import distance_to
from nqp.world_elements.entity_components import Allegiance, HealReceived, IsDead, IsReadyToAttack, Position, Stats
if TYPE_CHECKING:
    from nqp.core.game import Game
__all__ = ["BasicEntityBehaviour"]
[docs]class BasicEntityBehaviour(EntityBehaviour):
[docs]    def __init__(self, game: Game, unit: Unit, entity: EntityID):
        super().__init__(game, unit, entity) 
[docs]    def update(self, delta_time: float):
        self.last_path_update += delta_time
        if self.state == "path_fast":
            self.new_move_speed = 500
        else:
            self.reset_move_speed = True
        # check for new orders
        if self.target_unit != self._unit.behaviour.target_unit:
            self.target_unit = self._unit.behaviour.target_unit
            self.update_target_entity()
        if self.target_entity:
            is_alive = not snecs.has_component(self.target_entity, IsDead)
            if (self.target_entity not in self._unit.behaviour.valid_targets) or (not is_alive):
                self.update_target_entity()
            self.determine_next_action(True)
        # update path
        if self.last_path_update > PATH_UPDATE_FREQ:
            self.update_path()
        # apply and reset regen, if needed
        if self.regen_timer < 0:
            self.apply_regen()
            self.regen_timer = 1
        # update timers
        self.attack_timer -= delta_time
        self.regen_timer -= delta_time 
[docs]    def determine_next_action(self, focus_entity: bool):
        """
        Determine what to do next, i.e. what state to transition into.
        If focus_entity is True then should a target position exist it
        will be overwritten by the target entity's position.
        """
        stats = snecs.entity_component(self._entity, Stats)
        pos = snecs.entity_component(self._entity, Position)
        target_stats = snecs.entity_component(self.target_entity, Stats)
        # update target pos
        if focus_entity or self.target_position is None:
            target_pos = snecs.entity_component(self.target_entity, Position).pos
            self.target_position = target_pos
        else:
            target_pos = self.target_position
        # check distance to target
        if (stats.range.value + stats.size.value + target_stats.size.value < distance_to(pos.pos, target_pos)) or (
            self._unit.behaviour.check_visibility and not self.visibility_line
        ):
            self.state = self.movement_mode
        else:
            self.state = "idle"
        # attack intent
        if ((not self._unit.behaviour.check_visibility) or self.visibility_line) and self.attack_timer <= 0:
            # ensure doesnt al ready have component
            if not snecs.has_component(self._entity, IsReadyToAttack):
                snecs.add_component(self._entity, IsReadyToAttack()) 
[docs]    def update_path(self):
        """
        Update pathing to target.
        """
        # decrement timer
        self.last_path_update -= PATH_UPDATE_FREQ
        # check if can see target
        if self.target_entity:
            pos = snecs.entity_component(self._entity, Position)
            target_pos = snecs.entity_component(self.target_entity, Position)
            self.visibility_line = self._game.world.model.terrain.sight_line(pos.pos, target_pos.pos)
        else:
            self.visibility_line = False
        # if we cant see target, move somewhere new
        if not self.visibility_line:
            if self._unit.behaviour.smart_range_retarget:
                team = snecs.entity_component(self._entity, Allegiance).team
                stats = snecs.entity_component(self._entity, Stats)
                pos = snecs.entity_component(self._entity, Position)
                for entity in self._game.world.model.get_all_entities():
                    target_team = snecs.entity_component(entity, Allegiance).team
                    # find entities on different team
                    if target_team != team:
                        target_pos = snecs.entity_component(self.target_entity, Position)
                        target_stats = snecs.entity_component(entity, Stats)
                        # check if in range
                        if stats.range.value + stats.size.value + target_stats.size.value < distance_to(
                            pos.pos, target_pos.pos
                        ):
                            # check target is visible
                            if self._game.world.model.terrain.sight_line(target_pos.pos, pos.pos):
                                # new target found, update info and stop searching
                                self.target_entity = entity
                                self.target_position = target_pos.pos
                                break
        if self.state == "path" or self.state == "path_fast":
            if self._unit.behaviour.regrouping:
                self.target_position = self._unit.behaviour.leader.pos
            if self._unit.behaviour.retreating:
                self.target_position = list(self._unit.behaviour.retreat_target)
            if self.target_position:
                pos = snecs.entity_component(self._entity, Position)
                self.current_path = self._game.world.model.terrain.pathfind_px(pos.pos, self.target_position) 
[docs]    def apply_regen(self):
        """
        Create heal component on entity
        """
        stats = snecs.entity_component(self._entity, Stats)
        # try to apply
        try:
            snecs.add_component(self._entity, HealReceived(stats.regen.value, HealingSource.SELF))
        except ValueError:
            heal_received = snecs.entity_component(self._entity, HealReceived)
            heal_received.add_heal(stats.regen.value, HealingSource.SELF)