package de.siphalor.was.game;

import de.siphalor.was.content.product.Product;
import de.siphalor.was.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Represent a single storage slot.
 */
public class StorageSlot {
	/**
	 * The depth of the slot
	 */
	final int depth;
	/**
	 * The products in this slot
	 */
	final Product[] products;

	/**
	 * Constructs a new storage slot.
	 * @param depth The depth of this slot
	 */
	public StorageSlot(int depth) {
		this.depth = depth;
		this.products = new Product[depth];
	}

	/**
	 * Gets an array of the products.
	 * @return The product array. Some entries might be <code>null</code> to indicate empty storage
	 */
	@NotNull
	public Product[] getProducts() {
		return products;
	}

	/**
	 * Gets all stored products indexed by their visual position (depth-dependent).
	 * @return A collection of indexed products
	 */
	@NotNull
	public Collection<Pair<@NotNull Integer, @NotNull Product>> getProductsIndexed() {
		Queue<Pair<Integer, Product>> queue = new ConcurrentLinkedQueue<>();
		int index = 0;
		for (int i = 0; i < depth; i++) {
			if (products[i] == null) {
				break;
			}

			queue.add(Pair.of(index, products[i]));
			index += products[i].getDepth();
		}
		return queue;
	}

	/**
	 * Removes all products from this slot.
	 */
	public void clear() {
		Arrays.fill(products, null);
	}

	/**
	 * Gets the visually first product.
	 * @return The product
	 */
	@Nullable
	public Product front() {
		for (int i = products.length - 1; i >= 0; i--) {
			Product product = products[i];
			if (product != null) {
				return product;
			}
		}
		return null;
	}

	/**
	 * Removes the visually first product.
	 * @return The visual position where this product was removed from or <code>-1</code> if there was no product to remove
	 */
	public int pop() {
		if (products[0] == null)
			return -1;
		int d = 0;
		for (int i = 1; i < products.length; i++) {
			if (products[i] == null) {
				products[i - 1] = null;
				return d;
			}
			d += products[i - 1].getDepth();
		}
		products[products.length - 1] = null;
		return d;
	}

	/**
	 * Returns whether the given product fits into this slot depth-wise.
	 * @param product The product to check
	 * @return Returns whether it fits
	 */
	public boolean fits(@NotNull Product product) {
		int blocked = 0;
		for (Product p : products) {
			if (p == null) break;
			blocked += p.getDepth();
		}

		return product.getDepth() <= depth - blocked;
	}

	/**
	 * Adds a new product to the visual front of this slot.
	 * @param product The product to add
	 * @return The visual position where the product went or <code>-1</code> if insertion failed
	 */
	public int add(@NotNull Product product) {
		int blocked = 0;
		int i;
		for (i = 0; i < products.length; i++) {
			Product p = products[i];
			if (p == null) {
				break;
			}
			blocked += p.getDepth();
			if (blocked >= depth) {
				return -1;
			}
		}
		if (i < products.length && product.getDepth() <= depth - blocked) {
			products[i] = product;
			return blocked;
		}
		return -1;
	}
}
