package speiger.src.coreengine.utils.collections.registries.simple;

import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2IntMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.utils.ObjectCollections;
import speiger.src.collections.objects.utils.ObjectSets;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.utils.collections.iterators.ReadOnlyIterator;

public class SimpleRegistry<T extends IRegistryEntry<T>> implements IRegistry<T> {
	protected Object2IntMap<AssetLocation> nameToId = new Object2IntLinkedOpenHashMap<>();
	protected Object2ObjectMap<AssetLocation, T> nameToValue = new Object2ObjectLinkedOpenHashMap<>();
	protected ObjectList<AssetLocation> idsToName = new ObjectArrayList<>();
	protected BitSet freeIds;
	protected int capacity;
	protected int stored;
	boolean frozen = false;
	Object2IntMap<AssetLocation> missing = new Object2IntLinkedOpenHashMap<>();
	Object2ObjectMap<AssetLocation, AssetLocation> remaps = new Object2ObjectLinkedOpenHashMap<>();
	
	public SimpleRegistry(int capacity) {
		this.capacity = capacity;
		freeIds = new BitSet(capacity + 1);
		nameToId.setDefaultReturnValue(-1);
	}
	
	@Override
	public T register(T value) {
		if(frozen) throw new RuntimeException("Registry is Frozen");
		if(value.getName() == null) throw new IllegalStateException(value+", is Missing a name");
		AssetLocation name = value.getName();
		if(nameToValue.get(name) != null) throw new IllegalStateException("Name is already registered [Name="+name+", Tile="+nameToValue.get(name)+"]");
		int freeBit = freeIds.nextClearBit(0);
		if(capacity >= 0 && freeBit >= capacity) throw new IllegalStateException("Registry is full");
		while(idsToName.size() <= freeBit) {
			idsToName.add(null);
		}
		freeIds.set(freeBit);
		idsToName.set(freeBit, name);
		nameToValue.put(name, value);
		nameToId.put(name, freeBit);
		value.setId(this, freeBit);
		if(missing.size() > 0) missing.remove(name);
		stored++;
		return value;
	}
	
	@Override
	public int getId(AssetLocation name) { return nameToId.getInt(remap(name)); }
	@Override
	public AssetLocation getName(int id) { return idsToName.get(id); }
	@Override
	public T getValue(AssetLocation name) { return nameToValue.get(remap(name)); }
	public boolean isEmpty() { return stored <= 0; }
	public int size() { return stored; }
	@Override
	public Set<AssetLocation> keySet() { return ObjectSets.unmodifiable(nameToId.keySet()); }
	@Override
	public Collection<T> values() { return ObjectCollections.unmodifiable(nameToValue.values()); }
	@Override
	public Iterator<T> iterator() { return new ReadOnlyIterator<T>(nameToValue.values().iterator()); }
	
	protected AssetLocation remap(AssetLocation location) {
		AssetLocation mapped = remaps.get(location);
		return mapped == null ? location : mapped;
	}
	public final void freeze() { frozen = true; }
}
