package com.hearthsim.util;

import com.hearthsim.Game;
import com.hearthsim.card.Card;
import com.hearthsim.card.Deck;
import com.hearthsim.card.minion.Hero;
import com.hearthsim.card.minion.Minion;
import com.hearthsim.exception.HSException;
import com.hearthsim.model.PlayerModel;
import com.hearthsim.model.PlayerSide;
import com.hearthsim.util.tree.HearthTreeNode;


/**
 * A class representing an action that a player can take
 *
 *
 */
public class HearthAction {

    // TODO the DO_NOT_ verbs are used for history tracking but we can probably optimize them away in the future.
    public enum Verb {
        USE_CARD, HERO_ABILITY, ATTACK, UNTARGETABLE_BATTLECRY, TARGETABLE_BATTLECRY, START_TURN, END_TURN, DO_NOT_USE_CARD, DO_NOT_ATTACK, DO_NOT_USE_HEROPOWER, RNG, DRAW_CARDS
    }

    public final Verb verb_;

    public final PlayerSide actionPerformerPlayerSide;
    public final int cardOrCharacterIndex_;

    public final PlayerSide targetPlayerSide;
    public final int targetCharacterIndex_;

    public HearthAction(Verb verb) {
        this(verb, PlayerSide.CURRENT_PLAYER, -1, null, -1);
    }

    public HearthAction(Verb verb, PlayerSide actionPerformerPlayerSide, int cardOrCharacterIndex) {
        this(verb, actionPerformerPlayerSide, cardOrCharacterIndex, null, -1);
    }

    public HearthAction(Verb verb, PlayerSide actionPerformerPlayerSide, int cardOrCharacterIndex, PlayerSide targetPlayerSide, int targetCharacterIndex) {
        verb_ = verb;
        this.actionPerformerPlayerSide = actionPerformerPlayerSide;
        cardOrCharacterIndex_ = cardOrCharacterIndex;

        this.targetPlayerSide = targetPlayerSide;
        targetCharacterIndex_ = targetCharacterIndex;
    }

    @Deprecated
    public HearthTreeNode perform(HearthTreeNode boardState, Deck deckPlayer0, Deck deckPlayer1) throws HSException {
        return this.perform(boardState, true);
    }

    @Deprecated
    public HearthTreeNode perform(HearthTreeNode boardState, Deck deckPlayer0, Deck deckPlayer1, boolean singleRealization) throws HSException {
        return this.perform(boardState, singleRealization);
    }

    public HearthTreeNode perform(HearthTreeNode boardState) throws HSException {
        return this.perform(boardState, true);
    }

    public HearthTreeNode perform(HearthTreeNode boardState, boolean singleRealization) throws HSException {
        HearthTreeNode toRet = boardState;
        PlayerModel actingPlayer = actionPerformerPlayerSide != null ? boardState.data_.modelForSide(actionPerformerPlayerSide) : null;
        PlayerModel targetPlayer = targetPlayerSide != null ? boardState.data_.modelForSide(targetPlayerSide) : null;

        switch(verb_) {
            case USE_CARD: {
                Card card = actingPlayer.getHand().get(cardOrCharacterIndex_);
                toRet = card.useOn(targetPlayerSide, targetCharacterIndex_, toRet, singleRealization);
            }
            break;
            case HERO_ABILITY: {
                Hero hero = actingPlayer.getHero();
                Minion target = targetPlayer.getCharacter(targetCharacterIndex_);
                toRet = hero.useHeroAbility(targetPlayerSide, target, toRet, singleRealization);
            }
            break;
            case ATTACK: {
                Minion attacker = actingPlayer.getCharacter(cardOrCharacterIndex_);
                toRet = attacker.attack(targetPlayerSide, targetCharacterIndex_, toRet, singleRealization);
            }
            break;
            case UNTARGETABLE_BATTLECRY: {
                Minion minion = actingPlayer.getCharacter(cardOrCharacterIndex_);
                Minion placementTarget = targetPlayer.getCharacter(targetCharacterIndex_);
                toRet = minion.useUntargetableBattlecry(placementTarget, toRet, singleRealization);
                break;
            }
            case TARGETABLE_BATTLECRY: {
                Minion minion = actingPlayer.getCharacter(cardOrCharacterIndex_);
                Minion battlecryTarget = targetPlayer.getCharacter(targetCharacterIndex_);
                toRet = minion.useTargetableBattlecry(targetPlayerSide, battlecryTarget, toRet, singleRealization);
                break;
            }
            case START_TURN: {
                toRet = new HearthTreeNode(Game.beginTurn(boardState.data_.deepCopy()));
                break;
            }
            case END_TURN: {
                toRet = new HearthTreeNode(Game.endTurn(boardState.data_.deepCopy()).flipPlayers());
                break;
            }
            case DO_NOT_USE_CARD: {
                for (Card c : actingPlayer.getHand()) {
                    c.hasBeenUsed(true);
                }
                break;
            }
            case DO_NOT_ATTACK: {
                for (Minion minion : actingPlayer.getMinions()) {
                    minion.hasAttacked(true);
                }
                actingPlayer.getHero().hasAttacked(true);
                break;
            }
            case DO_NOT_USE_HEROPOWER: {
                actingPlayer.getHero().hasBeenUsed(true);
                break;
            }
            case RNG: {
                // We need to perform the current state again if the children don't exist yet. This can happen in certain replay scenarios.
                // Do not do this if the previous action was *also* RNG or we will end up in an infinite loop.
                if (toRet.isLeaf() && boardState.getAction().verb_ != Verb.RNG) {
                    boardState.data_.getCurrentPlayer().addNumCardsUsed((byte)-1); //do not double count
                    toRet = boardState.getAction().perform(boardState, singleRealization);
                }
                // RNG has declared this child happened
                toRet = toRet.getChildren().get(cardOrCharacterIndex_);
                break;
            }
            case DRAW_CARDS: {
                // Note, this action only supports drawing cards from the deck. Cards like Ysera or Webspinner need to be implemented using RNG children.
                for (int indx = 0; indx < cardOrCharacterIndex_; ++indx) {
                    actingPlayer.drawNextCardFromDeck();
                }
                break;
            }
        }
        return toRet;
    }
}

@@ -363,16 +363,44 @@
 eeNode;%0A
+import org.json.JSONObject;%0A
 %0A%0A/**%0A *
@@ -1732,32 +1732,435 @@
 erIndex;%0A    %7D%0A%0A
+    public JSONObject toJSON() %7B%0A        JSONObject json = new JSONObject();%0A%0A        json.put(%22verb_%22, verb_);%0A        json.put(%22actionPerformerPlayerSide%22, actionPerformerPlayerSide);%0A        json.put(%22cardOrCharacterIndex_%22, cardOrCharacterIndex_);%0A        json.put(%22targetPlayerSide%22, targetPlayerSide);%0A        json.put(%22targetCharacterIndex_%22, targetCharacterIndex_);%0A%0A        return json;%0A    %7D%0A%0A
     @Deprecated%0A

Add toJSON to HearthAction