package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.StaticAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.watchers.Watcher;
import java.util.ArrayList;
import java.util.List;
/**
* 702.165. Backup
*
* 702.165a Backup is a triggered ability. “Backup N” means “When this creature enters the battlefield,
* put N +1/+1 counters on target creature. If that’s another creature, it also gains the non-backup
* abilities of this creature printed below this one until end of turn.” Cards with backup have one or more
* abilities printed after the backup ability. (Some cards with backup also have abilities printed before
* the backup ability.)
*
* 702.165b If a permanent enters the battlefield as a copy of a permanent with a backup ability or a token
* is created that is a copy of that permanent, the order of abilities printed on it is maintained.
*
* 702.165c Only abilities printed on the object with backup are granted by its backup ability. Any abilities
* gained by a permanent, whether due to a copy effect, an effect that grants an ability to a permanent, or an
* effect that creates a token with certain abilities, are not granted by a backup ability.
*
* 702.165d The abilities that a backup ability grants are determined as the ability is put on the stack.
* They won’t change if the permanent with backup loses any abilities after the ability is put on the stack
* but before it resolves.
*
* @author TheElk801
*/
public class BackupAbility extends EntersBattlefieldTriggeredAbility {
private final Card card;
private final int amount;
private final List abilitiesToAdd = new ArrayList<>();
public BackupAbility(Card card, int amount) {
super(null, false);
this.addEffect(new BackupEffect(amount, abilitiesToAdd));
this.card = card;
this.amount = amount;
this.addTarget(new TargetCreaturePermanent());
}
protected BackupAbility(final BackupAbility ability) {
super(ability);
this.amount = ability.amount;
this.card = ability.card;
this.abilitiesToAdd.addAll(ability.abilitiesToAdd);
}
@Override
public BackupAbility copy() {
return new BackupAbility(this);
}
@Override
public String getRule() {
return "Backup " + amount + " (When this creature enters the battlefield," +
" put " + CardUtil.getOneOneCountersText(amount) +
" on target creature. If that's another creature, it gains the following" +
" abilit" + (abilitiesToAdd.size() > 1 ? "ies" : "y") + " until end of turn.)";
}
public void addAbility(Ability ability) {
addAbility(ability, null);
}
public void addAbility(Ability ability, Watcher watcher) {
addAbility(ability, watcher, false);
}
public void addAbility(Ability ability, boolean dontAddToCard) {
addAbility(ability, null, dontAddToCard);
}
/**
* @param ability
* @param watcher
* @param dontAddToCard use it on multiple instances of backups (example: Conclave Sledge-Captain)
*/
public void addAbility(Ability ability, Watcher watcher, boolean dontAddToCard) {
// runtime/verify check: wrong duration in backup's static effects
if (ability instanceof StaticAbility) {
Effect wrongEffect = ability.getEffects().stream()
.filter(effect -> effect instanceof ContinuousEffect)
.map(effect -> (ContinuousEffect) effect)
.filter(effect -> effect.getDuration().equals(Duration.EndOfTurn))
.findFirst()
.orElse(null);
if (wrongEffect != null) {
// how-to fix: add effect with Duration.EndOfGame (backup's abilities for permanent controls by GainAbility)
throw new IllegalArgumentException("Wrong code usage. Backup ability and source card must contains static effects. Wrong effect: " + wrongEffect.getClass().getName());
}
}
if (watcher != null) {
ability.addWatcher(watcher);
}
// parent card must have only 1 instance of ability
if (!dontAddToCard) {
card.addAbility(ability);
}
// target permanent can have multiple instances of ability
abilitiesToAdd.add(ability);
CardUtil.castStream(this.getEffects().stream(), BackupEffect.class)
.forEach(backupEffect -> backupEffect.addAbility(ability));
}
public boolean hasAbilities() {
return !abilitiesToAdd.isEmpty();
}
}
class BackupEffect extends OneShotEffect {
private final int amount;
private final List abilitiesToAdd = new ArrayList<>();
public BackupEffect(int amount, List abilitiesToAdd) {
super(Outcome.BoostCreature);
this.amount = amount;
this.abilitiesToAdd.addAll(abilitiesToAdd);
}
protected BackupEffect(final BackupEffect effect) {
super(effect);
this.amount = effect.amount;
this.abilitiesToAdd.addAll(effect.abilitiesToAdd);
}
void addAbility(Ability ability) {
this.abilitiesToAdd.add(ability);
}
@Override
public BackupEffect copy() {
return new BackupEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent == null) {
return false;
}
permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game);
if (permanent.getId().equals(source.getSourceId())) {
return true;
}
for (Ability ability : abilitiesToAdd) {
game.addEffect(new GainAbilityTargetEffect(ability).setTargetPointer(new FixedTarget(permanent, game)), source);
}
return true;
}
}