package amidst.mojangapi.world.icon.producer;

import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

import amidst.documentation.ThreadSafe;
import amidst.fragment.Fragment;
import amidst.mojangapi.world.coordinates.CoordinatesInWorld;
import amidst.mojangapi.world.icon.WorldIcon;

@ThreadSafe
public abstract class CachedWorldIconProducer extends WorldIconProducer<Void> {
	private final Object cacheLock = new Object();
	private volatile List<WorldIcon> cache;

	@Override
	public void produce(CoordinatesInWorld corner, Consumer<WorldIcon> consumer, Void additionalData) {
		for (WorldIcon icon : getCache()) {
			if (icon.getCoordinates().isInBoundsOf(corner, Fragment.SIZE)) {
				consumer.accept(icon);
			}
		}
	}

	public List<WorldIcon> getWorldIcons() {
		return getCache();
	}

	public WorldIcon getFirstWorldIcon() {
		List<WorldIcon> cache = getCache();
		if (cache.isEmpty()) {
			return null;
		} else {
			return cache.get(0);
		}
	}

	public void resetCache() {
		cache = null;
	}

	/**
	 * Gets the list of WorldIcons. Returns the cache and creates it, if
	 * necessary. This will never return null. This also ensures that
	 * createCache is only called by one thread at a time.
	 */
	private List<WorldIcon> getCache() {
		List<WorldIcon> result = cache;
		if (result == null) {
			synchronized (cacheLock) {
				if (cache == null) {
					cache = createCache();
				}
				result = cache;
			}
		}
		return result;
	}

	/**
	 * Creates the list of WorldIcons. This will never return null.
	 */
	private List<WorldIcon> createCache() {
		List<WorldIcon> result = doCreateCache();
		if (result == null) {
			return Collections.emptyList();
		} else {
			return Collections.unmodifiableList(result);
		}
	}

	/**
	 * This actually create the cache. This can return null. This will only be
	 * called by one thread at a time.
	 */
	protected abstract List<WorldIcon> doCreateCache();
}