package net.zomis.cardshifter.ecs.effects;

import java.util.function.*;

import com.cardshifter.modapi.base.Component;
import com.cardshifter.modapi.base.ECSGame;
import com.cardshifter.modapi.base.ECSSystem;
import com.cardshifter.modapi.base.Entity;
import com.cardshifter.modapi.cards.BattlefieldComponent;
import com.cardshifter.modapi.cards.ZoneChangeEvent;
import com.cardshifter.modapi.events.EntityRemoveEvent;
import com.cardshifter.modapi.events.IEvent;
import com.cardshifter.modapi.resources.ECSResource;
import com.cardshifter.modapi.resources.ECSResourceData;
import com.cardshifter.modapi.resources.ResourceRetriever;

public class Effects {

    public <T extends IEvent> Function<Entity, ECSSystem> triggerSystem(Class<T> eventClass, BiPredicate<Entity, T> interestingEvents, BiConsumer<Entity, T> handler) {
        return e -> new ECSSystem() {
            @Override
            public void startGame(ECSGame game) {
                game.getEvents().registerHandlerAfter(this, eventClass, this::event);
            }

            private void event(T event) {
                if (interestingEvents.test(e, event)) {
                    handler.accept(e, event);
                }
            }
        };
    }

    public <T extends IEvent> Function<Entity, ECSSystem> triggerSystemBefore(Class<T> eventClass, BiPredicate<Entity, T> interestingEvents, BiConsumer<Entity, T> handler) {
        return e -> new ECSSystem() {
            @Override
            public void startGame(ECSGame game) {
                game.getEvents().registerHandlerBefore(this, eventClass, this::event);
            }

            private void event(T event) {
                if (interestingEvents.test(e, event)) {
                    handler.accept(e, event);
                }
            }
        };
    }

	public EffectComponent giveTarget(ECSResource resource, int value, IntUnaryOperator operator) {
		ResourceRetriever res = ResourceRetriever.forResource(resource);
		GameEffect effect = (entity, event) -> event.getAction().getAllTargets().forEach(e -> {
			ECSResourceData data = res.resFor(e);
			data.change(value);
			data.set(operator.applyAsInt(data.get()));
		});
		return new EffectComponent("Give target " + value + " " + resource, effect);
	}
	public <T extends IEvent> EffectComponent giveSelf(Function<Entity, ECSSystem> system) {
		GameEffect effect = (entity, event) -> entity.getGame().addSystem(new InGameSystem(entity, system.apply(entity)));
		return new EffectComponent("Give target " + system, effect);
	}

	public EffectComponent toSelf(Consumer<Entity> effect) {
		return new EffectComponent(effect.toString(), (entity, event) -> effect.accept(event.getEntity()));
	}

	public Component described(String description, EffectComponent effectComponent) {
		return new EffectComponent(description, effectComponent.getEffect());
	}

	public static class InGameSystem implements ECSSystem {

		private final Entity owningEntity;
		private final ECSSystem systemToRemove;

		public InGameSystem(Entity owningEntity, ECSSystem system) {
			this.owningEntity = owningEntity;
			this.systemToRemove = system;
		}

		@Override
		public void startGame(ECSGame game) {
			game.getEvents().registerHandlerAfter(systemToRemove, EntityRemoveEvent.class, this::removeCheck);
			game.getEvents().registerHandlerAfter(systemToRemove, ZoneChangeEvent.class, this::removeCheck);
			game.addSystem(systemToRemove);
		}
		
		private void removeCheck(EntityRemoveEvent event) {
			if (event.getEntity() == owningEntity) {
				event.getEntity().getGame().removeSystem(systemToRemove);
			}
		}
		
		private void removeCheck(ZoneChangeEvent event) {
			if (event.getDestination() instanceof BattlefieldComponent) {
				return;
			}
			if (event.getCard() == owningEntity) {
				event.getCard().getGame().removeSystem(systemToRemove);
			}
		}
	}

}

@@ -2671,32 +2671,21 @@
 accept(e
-vent.getE
 ntity
-()
 ));%0A%09%7D%0A%0A

Fix NPE bug in #446