/// Card effect resolution engine

pub fn deal_damage_to_enemy(state : BattleState, raw_damage : Int, piercing : Bool) -> BattleState {
  if raw_damage <= 0 { return state }
  let mut actual = raw_damage
  let mut ea = state.enemy.armor
  if !piercing && ea > 0 {
    if ea >= actual { ea = ea - actual; actual = 0 }
    else { actual = actual - ea; ea = 0 }
  }
  let nh = if actual > 0 { if state.enemy.hp - actual < 0 { 0 } else { state.enemy.hp - actual } } else { state.enemy.hp }
  { ..state, enemy: { ..state.enemy, hp: nh, armor: ea } }
}

pub fn deal_damage_to_player(state : BattleState, damage : Int, piercing : Bool) -> BattleState {
  if piercing {
    let nh = if state.player.hp - damage < 0 { 0 } else { state.player.hp - damage }
    return { ..state, player: { ..state.player, hp: nh } }
  }
  let mut actual = damage
  let mut pa = state.player.armor
  if pa > 0 {
    if pa >= actual { pa = pa - actual; actual = 0 }
    else { actual = actual - pa; pa = 0 }
  }
  let nh = if actual > 0 { if state.player.hp - actual < 0 { 0 } else { state.player.hp - actual } } else { state.player.hp }
  { ..state, player: { ..state.player, hp: nh, armor: pa } }
}

pub fn add_player_armor(state : BattleState, amount : Int) -> BattleState {
  { ..state, player: { ..state.player, armor: state.player.armor + amount } }
}

pub fn add_enemy_armor(state : BattleState, amount : Int) -> BattleState {
  { ..state, enemy: { ..state.enemy, armor: state.enemy.armor + amount } }
}

pub fn add_sonic_boom(state : BattleState, stacks : Int) -> BattleState {
  let nd : Array[Debuff] = []
  let mut found = false
  let mut i = 0
  while i < state.enemy.debuffs.length() {
    let d = state.enemy.debuffs[i]
    if d.debuff_type == SonicBoom {
      nd.push({ ..d, stacks: d.stacks + stacks })
      found = true
    } else { nd.push(d) }
    i = i + 1
  }
  if !found { nd.push({ debuff_type: SonicBoom, stacks: stacks }) }
  { ..state, enemy: { ..state.enemy, debuffs: nd } }
}

pub fn check_victory(state : BattleState) -> BattleState {
  if state.enemy.hp <= 0 { { ..state, phase: Victory_ } } else { state }
}

pub fn check_defeat(state : BattleState) -> BattleState {
  if state.player.hp <= 0 { { ..state, phase: Defeat_ } }
  else { if is_pollution_max(state.pollution) { { ..state, phase: Defeat_ } } else { state } }
}

pub fn play_card(state : BattleState, card_index : Int) -> BattleState {
  if card_index < 0 || card_index >= state.player.hand.length() { return add_log(state, "Invalid card") }
  let card = state.player.hand[card_index]
  let free = state.player.free_second_attack
  if !free && card.cost > state.player.ap { return add_log(state, "Not enough AP") }
  let mut s = state

  // Remove from hand
  let nh : Array[Card] = []
  let mut i = 0
  while i < s.player.hand.length() {
    if i != card_index { nh.push(s.player.hand[i]) }
    i = i + 1
  }
  s = { ..s, player: { ..s.player, hand: nh } }

  // Discard or exile
  if card.exhaust {
    let ne : Array[Card] = []
    let mut j = 0
    while j < s.player.exiled.length() { ne.push(s.player.exiled[j]); j = j + 1 }
    ne.push(card)
    s = { ..s, player: { ..s.player, exiled: ne } }
  } else {
    let nd : Array[Card] = []
    let mut k = 0
    while k < s.player.discard.length() { nd.push(s.player.discard[k]); k = k + 1 }
    nd.push(card)
    s = { ..s, player: { ..s.player, discard: nd } }
  }

  // Pay AP
  if free { s = { ..s, player: { ..s.player, free_second_attack: false } } }
  else { s = { ..s, player: { ..s.player, ap: s.player.ap - card.cost } } }

  // Apply effect
  s = apply_card_effect(s, card)

  // Count and harmonic
  s = { ..s, player: { ..s.player, cards_played_this_turn: s.player.cards_played_this_turn + 1 } }
  if s.player.harmonic_active && card.id != "zl-fortress-02" { s = add_player_armor(s, 2) }
  if card.card_type == Attack && card.id != "zl-overload-03" {
    s = { ..s, player: { ..s.player, next_attack_bonus: 0 } }
  }

  s = check_victory(s)
  s = check_defeat(s)
  add_log(s, "Play: " + card.name)
}

fn apply_card_effect(state : BattleState, card : Card) -> BattleState {
  let mut s = state
  let lv = get_pollution_level(s.pollution)
  let ed = lv.damage_bonus
  match card.id {
    "zl-fortress-01" => {
      s = add_player_armor(s, 14)
      if s.player.armor > 20 { s = deal_damage_to_enemy(s, s.player.armor - 20, false) }
      s
    }
    "zl-fortress-02" => {
      s = add_player_armor(s, 3)
      { ..s, player: { ..s.player, harmonic_active: true } }
    }
    "zl-fortress-03" => {
      let hd = s.player.armor / 2
      if hd > 0 { s = deal_damage_to_enemy(s, hd, false) }
      { ..s, player: { ..s.player, armor: s.player.armor / 2 } }
    }
    "zl-overload-01" => {
      let total = 16 + s.player.damage_bonus + ed + s.player.next_attack_bonus
      s = deal_damage_to_enemy(s, total, false)
      s = deal_damage_to_player(s, 5, true)
      { ..s, player: { ..s.player, has_taken_self_damage: true } }
    }
    "zl-overload-02" => {
      let mut base = 4 + s.player.damage_bonus + ed + s.player.next_attack_bonus
      if s.player.has_taken_self_damage {
        base = base * 2
        s = { ..s, player: { ..s.player, free_second_attack: true } }
      }
      deal_damage_to_enemy(s, base, false)
    }
    "zl-overload-03" => {
      s = deal_damage_to_player(s, 10, true)
      let nap = s.player.ap + 2
      let nbonus = s.player.next_attack_bonus + 10
      { ..s, player: { ..s.player, ap: nap, next_attack_bonus: nbonus } }
    }
    "zl-basic-09" => {
      s = add_player_armor(s, 3)
      s = draw_cards(s, 1)
      let np = if s.pollution - 10 < 0 { 0 } else { s.pollution - 10 }
      { ..s, pollution: np }
    }
    "zl-basic-10" => {
      let dmg = 3 + s.player.damage_bonus + ed + s.player.next_attack_bonus
      s = deal_damage_to_enemy(s, dmg, false)
      add_sonic_boom(s, 1)
    }
    "zl-ability-01" => {
      let ab = { id: "FREQ_ANCHOR", card_id: card.id, name: card.name, effect: card.effect }
      let abs : Array[ActiveAbility] = []
      let mut a = 0
      while a < s.player.permanent_abilities.length() { abs.push(s.player.permanent_abilities[a]); a = a + 1 }
      abs.push(ab)
      { ..s, player: { ..s.player, permanent_abilities: abs, armor_per_turn: s.player.armor_per_turn + 3 } }
    }
    "zl-ability-02" => {
      let ab = { id: "LOW_FREQ_RES", card_id: card.id, name: card.name, effect: card.effect }
      let abs : Array[ActiveAbility] = []
      let mut a = 0
      while a < s.player.permanent_abilities.length() { abs.push(s.player.permanent_abilities[a]); a = a + 1 }
      abs.push(ab)
      { ..s, player: { ..s.player, permanent_abilities: abs, extra_damage_per_armor: s.player.extra_damage_per_armor + 0.6 } }
    }
    "zl-ability-03" => {
      let ab = { id: "PAIN_ECHO", card_id: card.id, name: card.name, effect: card.effect }
      let abs : Array[ActiveAbility] = []
      let mut a = 0
      while a < s.player.permanent_abilities.length() { abs.push(s.player.permanent_abilities[a]); a = a + 1 }
      abs.push(ab)
      { ..s, player: { ..s.player, permanent_abilities: abs } }
    }
    "zl-ability-04" => {
      let ab = { id: "FINAL_NOTE", card_id: card.id, name: card.name, effect: card.effect }
      let abs : Array[ActiveAbility] = []
      let mut a = 0
      while a < s.player.permanent_abilities.length() { abs.push(s.player.permanent_abilities[a]); a = a + 1 }
      abs.push(ab)
      let bonus = if s.player.hp < 20 { 5 } else { 0 }
      { ..s, player: { ..s.player, permanent_abilities: abs, damage_bonus: s.player.damage_bonus + bonus } }
    }
    _ => {
      if card.base_damage > 0 {
        let mut dmg = card.base_damage + s.player.damage_bonus + ed + s.player.next_attack_bonus
        if s.player.extra_damage_per_armor > 0.0 {
          let extra = (s.player.armor.to_double() * s.player.extra_damage_per_armor).to_int()
          dmg = dmg + extra
        }
        s = deal_damage_to_enemy(s, dmg, false)
      }
      if card.self_damage > 0 {
        s = deal_damage_to_player(s, card.self_damage, true)
        let pb = if card.self_damage <= 8 { card.self_damage } else { 8 }
        s = { ..s, player: { ..s.player, has_taken_self_damage: true, next_attack_bonus: s.player.next_attack_bonus + pb } }
      }
      if card.base_armor > 0 { s = add_player_armor(s, card.base_armor) }
      if card.sonic_boom > 0 { s = add_sonic_boom(s, card.sonic_boom) }
      if card.pollution_modifier != 0 {
        let np = s.pollution + card.pollution_modifier
        let cl = if np < 0 { 0 } else { if np > 100 { 100 } else { np } }
        s = { ..s, pollution: cl }
      }
      s
    }
  }
}

pub fn end_player_turn(state : BattleState) -> BattleState {
  let mut s = state
  let mut i = 0
  while i < s.player.permanent_abilities.length() {
    if s.player.permanent_abilities[i].id == "FINAL_NOTE" {
      if s.player.hp < 20 { s = deal_damage_to_player(s, 2, true) }
      break
    }
    i = i + 1
  }
  { ..s, phase: EnemyTurn }
}

pub fn settle_sonic_boom(state : BattleState) -> BattleState {
  let mut s = state
  let rest : Array[Debuff] = []
  let mut i = 0
  while i < s.enemy.debuffs.length() {
    let d = s.enemy.debuffs[i]
    if d.debuff_type == SonicBoom && d.stacks > 0 {
      let dmg = d.stacks * 2
      let nh = if s.enemy.hp - dmg < 0 { 0 } else { s.enemy.hp - dmg }
      s = { ..s, enemy: { ..s.enemy, hp: nh } }
    } else { rest.push(d) }
    i = i + 1
  }
  s = { ..s, enemy: { ..s.enemy, debuffs: rest } }
  check_victory(s)
}

pub fn increase_pollution(state : BattleState, amount : Int) -> BattleState {
  let np = state.pollution + amount
  { ..state, pollution: if np > 100 { 100 } else { np } }
}