extends Node

func _process(delta: float) -> void:
  _process_player(delta)
  _process_entities(delta, EnemyData.entities)
  _process_entities(delta, BossData.entities)

func _process_player(delta: float) -> void:
  if not PlayerData.alive or PlayerData.max_shield <= 0:
    return
  if PlayerData.shield < PlayerData.max_shield:
    PlayerData.shield_regen_timer += delta
    if PlayerData.shield_regen_timer >= PlayerData.shield_regen_delay:
      var regen_rate: float = PlayerData.max_shield / PlayerData.shield_regen_time
      var shield: float = PlayerData.shield + regen_rate * delta
      if shield >= PlayerData.max_shield:
        shield = PlayerData.max_shield
        EventBus.shield_regenerated.emit(get_tree().get_first_node_in_group("player"))
      PlayerData.set_shield(shield)

func _process_entities(delta: float, entities: Dictionary) -> void:
  for entity in entities:
    if not is_instance_valid(entity):
      continue
    var data: Dictionary = entities[entity]
    if not data["alive"]:
      continue
    var max_shield: float = data["max_shield"]
    if max_shield <= 0:
      continue
    var shield: float = data["shield"]
    if shield < max_shield:
      data["shield_regen_timer"] += delta
      if data["shield_regen_timer"] >= data["shield_regen_delay"]:
        var regen_rate: float = max_shield / data["shield_regen_time"]
        shield += regen_rate * delta
        if shield >= max_shield:
          shield = max_shield
          EventBus.shield_regenerated.emit(entity)
        data["shield"] = shield
        EventBus.shield_changed.emit(entity, shield, max_shield)

func absorb(target: Node, amount: float) -> float:
  var player: Node = get_tree().get_first_node_in_group("player")
  if target == player:
    if PlayerData.shield <= 0:
      return amount
    PlayerData.shield_regen_timer = 0.0
    var absorbed: float = min(amount, PlayerData.shield)
    var shield: float = PlayerData.shield - absorbed
    PlayerData.set_shield(shield)
    if shield <= 0:
      EventBus.shield_broken.emit(target)
    return amount - absorbed
  var data_source: Node = _get_data_source(target)
  if not data_source:
    return amount
  var shield: float = data_source.get_stat(target, "shield")
  if shield == null or shield <= 0:
    return amount
  data_source.set_stat(target, "shield_regen_timer", 0.0)
  var absorbed: float = min(amount, shield)
  shield -= absorbed
  data_source.set_shield(target, shield)
  if shield <= 0:
    EventBus.shield_broken.emit(target)
  return amount - absorbed

func _get_data_source(entity: Node) -> Node:
  if entity.is_in_group("boss"):
    return BossData
  elif entity.is_in_group("enemies"):
    return EnemyData
  return null
