class_name BattleCutsceneAction
const DialogueAction = preload("res://cutscene/dialogue/DialogueAction.gd")

static var NARRATION:DialogueResource = preload("res://dialogue/battle/narration.dialogue")

# State object passed as extra_game_states so {{variable}} tokens resolve in the dialogue file.
class BattleNarrationState:
  var fighter_name:String
  var user_name:String
  var target_name:String
  var move_name:String
  var damage:int

  func _init(params:Dictionary) -> void:
    fighter_name = params.get('fighter_name', '')
    user_name = params.get('user_name', '')
    target_name = params.get('target_name', '')
    move_name = params.get('move_name', '')
    damage = params.get('damage', 0)

# Narrates and executes a single fighter's decision.
static func battleDecisionCallable(params:Dictionary) -> int:
  var decision:BattleDecision = params['decision']
  var state = BattleNarrationState.new({
    'user_name': decision.user.name if decision.user is PartyMember else 'Enemy',
    'move_name': decision.action.handle,
    'target_name': decision.targets[0].name if decision.targets[0] is PartyMember else 'Enemy',
  })

  var cutscene:Cutscene = params['cutscene']
  cutscene.addCallable(
    DialogueAction.getDialogueCallable(NARRATION, 'move_perform', [state]).merged(
      {'position': Cutscene.CUTSCENE_ADD_NEXT}, false
    )
  )
  decision.action.perform({'user': decision.user, 'targets': decision.targets})
  return Cutscene.CUTSCENE_CONTINUE

static func getBattleDecisionCallable(decision:BattleDecision) -> Dictionary:
  return {
    'function': battleDecisionCallable,
    'decision': decision,
  }

static func playerDecisionCallable(params:Dictionary) -> int:
  # Check end conditions
  var allPlayersDead:bool = true
  var allEnemiesDead:bool = true
  for fighter:BattleFighter in BATTLE.fighterMap.values():
    if fighter == null:
      continue
    if fighter.status == BattleFighter.Status.DEAD:
      continue
    if fighter.isPlayerControlled():
      allPlayersDead = false
    else:
      allEnemiesDead = false

  if allPlayersDead:
    params['cutscene'].addCallable(
      DialogueAction.getDialogueCallable(NARRATION, 'battle_defeat').merged(
        {'position': Cutscene.CUTSCENE_ADD_NEXT}, false
      )
    )
    return Cutscene.CUTSCENE_END

  if allEnemiesDead:
    params['cutscene'].addCallable(
      DialogueAction.getDialogueCallable(NARRATION, 'battle_victory').merged(
        {'position': Cutscene.CUTSCENE_ADD_NEXT}, false
      )
    )
    return Cutscene.CUTSCENE_END

  # Collect fighters
  var playerFighters:Array[BattleFighter] = []
  var aiFighters:Array[BattleFighter] = []
  for fighter:BattleFighter in BATTLE.fighterMap.values():
    if !fighter || !fighter.canPlayerMakeDecision():
      continue
    playerFighters.append(fighter)
  for fighter:BattleFighter in BATTLE.fighterMap.values():
    if !fighter || !fighter.canMakeDecision():
      continue
    if fighter in playerFighters:
      continue
    aiFighters.append(fighter)

  # Gather decisions
  var decisions:Array[BattleDecision] = []
  var playerDecisions = await BATTLE.battleScene.getBattleDecisions(playerFighters)
  decisions.append_array(playerDecisions)
  for fighter in aiFighters:
    var decision = fighter.getAIDecision()
    if decision != null:
      decisions.append(decision)

  # Sort by priority
  decisions.shuffle()
  decisions.sort_custom(func(a:BattleDecision, b:BattleDecision) -> int:
    var pa = a.getPriority()
    var pb = b.getPriority()
    if pa > pb: return -1
    elif pa < pb: return 1
    else: return 0
  )

  for decision in decisions:
    params['cutscene'].addCallable(getBattleDecisionCallable(decision))

  params['cutscene'].addCallable(getPlayerDecisionCallable())
  return Cutscene.CUTSCENE_CONTINUE

static func getPlayerDecisionCallable() -> Dictionary:
  return {
    'function': playerDecisionCallable,
  }
