package dev.kkorolyov.killstreek.item;

import dev.kkorolyov.pancake.component.Sprite;
import dev.kkorolyov.pancake.entity.Entity;
import dev.kkorolyov.pancake.math.WeightedDistribution;

import java.util.NoSuchElementException;
import java.util.function.Consumer;

/**
 * Applies effects to entities.
 */
public class Item {
	private final String name;
	private final Sprite sprite;
	private final WeightedDistribution<Consumer<Entity>> effects = new WeightedDistribution<>();
	private int uses;

	/**
	 * Constructs a new item with {@code 1} use.
	 * @see #Item(String, Sprite, int)
	 */
	public Item(String name, Sprite sprite) {
		this(name, sprite, 1);
	}
	/**
	 * Constructs a new item.
	 * @param name item name
	 * @param sprite item visual
	 * @param uses initial number of item uses
	 */
	public Item(String name, Sprite sprite, int uses) {
		this.name = name;
		this.sprite = sprite;
		setUses(uses);
	}

	/**
	 * If this item has remaining uses, applies a random effect from this item to an entity and decrements remaining uses by {@code 1}.
	 * @param entity entity receiving effect
	 * @return {@code true} if this item had at least 1 use and affected {@code entity}
	 * @throws NoSuchElementException if this item has remaining uses, but no effects
	 */
	public boolean apply(Entity entity) {
		if (isEmpty()) return false;

		effects.get().accept(entity);
		uses--;
		return true;
	}

	/**
	 * Adds an effect with weight 1 to this item.
	 * @param effect effect on entity
	 * @return {@code this}
	 */
	public Item addEffect(Consumer<Entity> effect) {
		return addEffect(effect, 1);
	}
	/**
	 * Adds an effect to this item.
	 * @param effect effect on entity
	 * @param weight effect frequency in relation to all other effects of this item
	 * @return {@code this}
	 */
	public Item addEffect(Consumer<Entity> effect, int weight) {
		effects.add(weight, effect);
		return this;
	}

	/** @return {@code true} if this item has no more uses remaining */
	public boolean isEmpty() {
		return getUses() <= 0;
	}

	/** @return item name */
	public String getName() {
		return name;
	}

	/** @return item sprite */
	public Sprite getSprite() {
		return sprite;
	}

	/** @return number of remaining uses */
	public int getUses() {
		return uses;
	}
	/**
	 * @param uses new number of remaining uses
	 * @return {@code this}
	 */
	public Item setUses(int uses) {
		this.uses = uses;
		return this;
	}
}

@@ -306,16 +306,25 @@
 %0Apublic 
+abstract 
 class It
@@ -328,16 +328,219 @@
  Item %7B%0A
+%09/**%09An effect which does nothing, used for padding the pool of random effects */%0A%09public static final Consumer%3CEntity%3E NOOP_EFFECT = entity -%3E %7B%7D;%0A%09private static int idCounter;%0A%0A%09private final int id;%0A
 %09private
@@ -686,27 +686,8 @@
 %3E();
-%0A%09private int uses;
 %0A%0A%09/
@@ -724,165 +724,19 @@
 ith 
-%7B@code 1%7D use.%0A%09 * @see #Item(String, Sprite, int)%0A%09 */%0A%09public Item(String name, Sprite sprite) %7B%0A%09%09this(name, sprite, 1);%0A%09%7D%0A%09/**%0A%09 * Constructs a new item
+a unique ID
 .%0A%09 
@@ -793,52 +793,8 @@
 ual%0A
-%09 * @param uses initial number of item uses%0A
 %09 */
@@ -837,21 +837,41 @@
 rite
-, int uses) %7B
+) %7B%0A%09%09this.id = Item.idCounter++;
 %0A%09%09t
@@ -916,71 +916,21 @@
 e;%0A%09
-%09setUses(uses);%0A%09%7D%0A%0A%09/**%0A%09 * If this item has remaining uses, a
+%7D%0A%0A%09/**%0A%09 * A
 ppli
@@ -936,24 +936,33 @@
 ies a random
+ly-select
  effect from
@@ -975,192 +975,78 @@
 item
- to an entity and decrements remaining uses by %7B@code 1%7D.%0A%09 * @param entity entity receiving effect%0A%09 * @return %7B@code true%7D if this item had at least 1 use and affected %7B@code entity%7D
+'s effect pool to an entity.%0A%09 * @param entity entity receiving effect
 %0A%09 *
@@ -1098,28 +1098,8 @@
 has 
-remaining uses, but 
 no e
@@ -1118,23 +1118,20 @@
 %09public 
-boolean
+void
  apply(E
@@ -1150,40 +1150,8 @@
 ) %7B%0A
-%09%09if (isEmpty()) return false;%0A%0A
 %09%09ef
@@ -1182,33 +1182,8 @@
 y);%0A
-%09%09uses--;%0A%09%09return true;%0A
 %09%7D%0A%0A
@@ -1696,115 +1696,59 @@
 urn 
-%7B@code true%7D if this item has no more uses remaining */%0A%09public boolean isEmpty() %7B%0A%09%09return getUses() %3C= 0
+unique item ID */%0A%09public int getId() %7B%0A%09%09return id
 ;%0A%09%7D
@@ -1912,16 +1912,24 @@
 @return 
+maximum 
 number o
@@ -1934,217 +1934,86 @@
  of 
-remaining uses */%0A%09public int getUses() %7B%0A%09%09return uses;%0A%09%7D%0A%09/**%0A%09 * @param uses new number of remaining uses%0A%09 * @return %7B@code this%7D%0A%09 */%0A%09public Item setUses(int uses) %7B%0A%09%09this.uses = uses;%0A%09%09return this;%0A%09%7D
+items of this type in a single stack */%0A%09public abstract int getMaxStackSize();
 %0A%7D%0A

Remove Item uses