from typing import Any

import colors

class BaseAction:
	entity: Any

	def __init__(self, entity):
		self.entity = entity

	def apply(self) -> bool:
		raise NotImplementedError()

class QuitAction(BaseAction):
	def __init__(self): #override the base __init__
		pass

	def apply(self) -> bool:
		raise SystemExit()


class WaitAction(BaseAction):
	def apply(self) -> bool:
		return True


class DirectionAction(BaseAction):
	def __init__(self, entity, xdir: int, ydir: int):
		super().__init__(entity)
		self.xdir = xdir
		self.ydir = ydir

	def apply(self) -> bool:
		raise NotImplementedError()


class MovementAction(DirectionAction):
	def apply(self) -> bool:
		dest_x = self.entity.x + self.xdir
		dest_y = self.entity.y + self.ydir

		#bounds and collision checks
		if not self.entity.floor_map.in_bounds(dest_x, dest_y):
			return False
		if not self.entity.floor_map.tiles["walkable"][dest_x, dest_y]:
			return False
		if self.entity.floor_map.get_entity_at(dest_x, dest_y, unwalkable_only=True) is not None:
			return False

		self.entity.set_pos(dest_x, dest_y)
		return True

class MeleeAction(DirectionAction):
	def apply(self) -> bool:
		dest_x = self.entity.x + self.xdir
		dest_y = self.entity.y + self.ydir

		target = self.entity.floor_map.get_actor_at(dest_x, dest_y)

		if not target:
			return False

		#apply damage
		damage = self.entity.fighter.attack - target.fighter.defense
		target.fighter.current_hp -= damage

		#calculate output
		engine = self.entity.floor_map.engine
		msg_text = f"{self.entity.name} attacked {target.name}"
		msg_color = colors.white

		if self.entity is engine.player:
			msg_color = colors.player_atk
		else:
			msg_color = colors.enemy_atk

		if damage > 0:
			msg_text += f" for {damage} damage"
		else:
			msg_text += f" but was ineffective"

		engine.message_log.add_message(text = msg_text, fg=msg_color)

		return True

class BumpAction(DirectionAction): #bad name, deal with it
	def apply(self) -> bool:
		dest_x = self.entity.x + self.xdir
		dest_y = self.entity.y + self.ydir

		if self.entity.floor_map.get_entity_at(dest_x, dest_y, unwalkable_only=True):
			return MeleeAction(self.entity, self.xdir, self.ydir).apply()
		else:
			return MovementAction(self.entity, self.xdir, self.ydir).apply()