package mage.abilities.effects.common;

import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.PutCards;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.util.CardUtil;

/**
 * @author LevelX, awjackson
 */
public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEffect {

    protected int numberToPick;
    protected PutCards putPickedCards;
    protected FilterCard filter;
    protected boolean revealPickedCards;
    protected boolean optional;
    protected boolean upTo;

    public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick,
                                              PutCards putPickedCards, PutCards putLookedCards) {
        this(numberOfCards, numberToPick, putPickedCards, putLookedCards, false);
    }

    public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, int numberToPick,
                                              PutCards putPickedCards, PutCards putLookedCards) {
        this(numberOfCards, numberToPick, putPickedCards, putLookedCards, false);
    }

    public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick,
                                              PutCards putPickedCards, PutCards putLookedCards, boolean upTo) {
        this(StaticValue.get(numberOfCards), numberToPick, putPickedCards, putLookedCards, upTo);
    }

    public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, int numberToPick,
                                              PutCards putPickedCards, PutCards putLookedCards, boolean upTo) {
        super(putPickedCards.getOutcome(), numberOfCards, putLookedCards);
        this.numberToPick = numberToPick;
        this.putPickedCards = putPickedCards;
        this.filter = (numberToPick > 1) ? StaticFilters.FILTER_CARD_CARDS : StaticFilters.FILTER_CARD_A;
        this.revealPickedCards = false;
        this.optional = false;
        this.upTo = upTo || numberToPick == Integer.MAX_VALUE;
    }

    public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick, FilterCard filter,
                                              PutCards putPickedCards, PutCards putLookedCards) {
        this(numberOfCards, numberToPick, filter, putPickedCards, putLookedCards, true);
    }

    public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, int numberToPick, FilterCard filter,
                                              PutCards putPickedCards, PutCards putLookedCards) {
        this(numberOfCards, numberToPick, filter, putPickedCards, putLookedCards, true);
    }

    public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick, FilterCard filter,
                                              PutCards putPickedCards, PutCards putLookedCards, boolean optional) {
        this(StaticValue.get(numberOfCards), numberToPick, filter, putPickedCards, putLookedCards, optional);
    }

    public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, int numberToPick, FilterCard filter,
                                              PutCards putPickedCards, PutCards putLookedCards, boolean optional) {
        super(putPickedCards.getOutcome(), numberOfCards, putLookedCards);
        this.numberToPick = numberToPick;
        this.putPickedCards = putPickedCards;
        this.filter = filter;
        this.revealPickedCards = !putPickedCards.getZone().isPublicZone();
        this.optional = optional;
        this.upTo = (numberToPick > 1);
    }

    protected LookLibraryAndPickControllerEffect(final LookLibraryAndPickControllerEffect effect) {
        super(effect);
        this.numberToPick = effect.numberToPick;
        this.putPickedCards = effect.putPickedCards;
        this.filter = effect.filter.copy();
        this.revealPickedCards = effect.revealPickedCards;
        this.optional = effect.optional;
        this.upTo = effect.upTo;
    }

    @Override
    public LookLibraryAndPickControllerEffect copy() {
        return new LookLibraryAndPickControllerEffect(this);
    }

    @Override
    protected boolean actionWithLookedCards(Game game, Ability source, Player player, Cards cards) {
        int number = Math.min(numberToPick, cards.count(filter, source.getControllerId(), source, game));
        if (number < 1
                || optional && !player.chooseUse(putPickedCards.getOutcome(), getMayText(), source, game)) {
            return actionWithPickedCards(game, source, player, new CardsImpl(), cards);
        }
        TargetCard target = new TargetCard((upTo ? 0 : number), number, Zone.LIBRARY, filter);
        target.withChooseHint(getChooseHint());
        if (!player.chooseTarget(putPickedCards.getOutcome(), cards, target, source, game)) {
            return actionWithPickedCards(game, source, player, new CardsImpl(), cards);
        }
        Cards pickedCards = new CardsImpl(target.getTargets());
        if (revealPickedCards) {
            player.revealCards(source, pickedCards, game);
        }
        cards.removeAll(pickedCards);
        return actionWithPickedCards(game, source, player, pickedCards, cards);
    }

    /**
     * Can be overriden for additional effect
     */
    protected boolean actionWithPickedCards(Game game, Ability source, Player player, Cards pickedCards, Cards otherCards) {
        boolean result = putPickedCards.moveCards(player, pickedCards, source, game);
        result |= putLookedCards.moveCards(player, otherCards, source, game);
        return result;
    }

    protected String getMayText() {
        boolean plural = numberToPick > 1;
        StringBuilder sb = new StringBuilder(revealPickedCards ? "Reveal " : "Put ");
        sb.append(plural ? filter.getMessage() : CardUtil.addArticle(filter.getMessage()));
        if (revealPickedCards) {
            sb.append(" and put ");
            sb.append(plural ? "them" : "it");
        }
        sb.append(" ");
        sb.append(putPickedCards.getMessage(false, plural));
        return sb.append("?").toString();
    }

    protected String getChooseHint() {
        return "to put " + putPickedCards.getMessage(false, numberToPick > 1);
    }

    @Override
    public String getText(Mode mode) {
        if (staticText != null && !staticText.isEmpty()) {
            return staticText;
        }
        StringBuilder sb = new StringBuilder(". ");
        if (optional) {
            sb.append(revealPickedCards ? "You may reveal " : "You may put ");
        } else {
            sb.append(revealPickedCards ? "Reveal " : "Put ");
        }
        boolean havePredicates = filter.hasPredicates();
        boolean plural = numberToPick > 1;
        if (havePredicates && !plural && !upTo) {
            sb.append(CardUtil.addArticle(filter.getMessage()));
        } else if (numberToPick == Integer.MAX_VALUE) {
            sb.append("any number of ");
            if (havePredicates) {
                sb.append(filter.getMessage());
            }
        } else {
            if (upTo) {
                sb.append("up to ");
            }
            sb.append(CardUtil.numberToText(numberToPick));
            sb.append(" ");
            sb.append(havePredicates ? filter.getMessage() : "of ");
        }
        if (havePredicates) {
            sb.append(" from among ");
        }
        sb.append("them ");
        if (revealPickedCards) {
            sb.append("and put ");
            sb.append(plural ? "them " : "it ");
        } else if (putPickedCards == PutCards.TOP_ANY && (numberOfCards instanceof StaticValue)) {
            sb.append("back ");
        }
        sb.append(putPickedCards.getMessage(false, plural));

        plural = optional
                || upTo
                || !(numberOfCards instanceof StaticValue)
                || numberOfCards.calculate(null, null, this) - numberToPick != 1;

        // if remaining text would be "put the other on top of your library", omit it
        if (!plural && putLookedCards == PutCards.TOP_ANY) {
            return setText(mode, sb.toString());
        }
        sb.append(havePredicates && (optional || upTo) ? ". Put" : " and");
        sb.append(" the ");
        sb.append(plural ? "rest " : "other ");
        if (putPickedCards == PutCards.GRAVEYARD && putLookedCards == PutCards.TOP_ANY) {
            sb.append("back ");
        }
        sb.append(putLookedCards.getMessage(false, plural));

        // get text frame from super class and inject action text
        return setText(mode, sb.toString());
    }
}
