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

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;
import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry;

public class MutableRegistry<T extends IRegistryEntry<T>> implements IMutableRegistry<T> {
	protected Object2IntMap<AssetLocation> nameToId = new Object2IntLinkedOpenHashMap<AssetLocation>();
	protected Object2ObjectMap<AssetLocation, T> nameToValue = new Object2ObjectLinkedOpenHashMap<AssetLocation, T>();
	protected ObjectList<AssetLocation> idsToName = new ObjectArrayList<AssetLocation>();
	protected BitSet freeIds;
	protected int capacity;
	protected int stored;
	
	public MutableRegistry(int capacity) {
		this.capacity = capacity;
		freeIds = new BitSet(capacity + 1);
		nameToId.setDefaultReturnValue(-1);
	}
	
	@Override
	public T register(T value) {
		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);
		stored++;
		return value;
	}
	
	@Override
	public boolean remove(int id) {
		if(freeIds.get(id)) {
			freeIds.clear(id);
			AssetLocation name = idsToName.get(id);
			idsToName.set(id, null);
			nameToId.rem(name);
			nameToValue.remove(name).setId(null, -1);
			stored--;
			return true;
		}
		return false;
	}
	
	@Override
	public int getId(AssetLocation name) { return nameToId.getInt(name); }
	@Override
	public AssetLocation getName(int id) { return idsToName.get(id); }
	@Override
	public T getValue(AssetLocation name) { return nameToValue.get(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()); }
}
