package org.terasology.entitySystem.pojo;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.terasology.entitySystem.*;
import org.terasology.entitySystem.common.NullIterator;
import org.terasology.entitySystem.event.AddComponentEvent;
import org.terasology.entitySystem.event.ChangedComponentEvent;
import org.terasology.entitySystem.event.RemovedComponentEvent;
import org.terasology.entitySystem.metadata.ComponentLibrary;
import org.terasology.entitySystem.metadata.extension.EntityRefTypeHandler;
import org.terasology.entitySystem.metadata.extension.PrefabTypeHandler;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Prototype entity manager. Not intended for final use, but a stand in for experimentation.
*
* @author Immortius <immortius@gmail.com>
*/
public class PojoEntityManager implements EntityManager, PersistableEntityManager {
public static final int NULL_ID = 0;
private static Logger logger = Logger.getLogger(PojoEntityManager.class.getName());
private int nextEntityId = 1;
private TIntList freedIds = new TIntArrayList();
private Map<EntityRef, PojoEntityRef> entityCache = new WeakHashMap<EntityRef, PojoEntityRef>();
private ComponentTable store = new ComponentTable();
private EventSystem eventSystem;
private PrefabManager prefabManager;
private ComponentLibrary componentLibrary;
public PojoEntityManager(ComponentLibrary componentLibrary, PrefabManager prefabManager) {
this.componentLibrary = componentLibrary;
this.prefabManager = prefabManager;
componentLibrary.registerTypeHandler(EntityRef.class, new EntityRefTypeHandler(this));
componentLibrary.registerTypeHandler(Prefab.class, new PrefabTypeHandler(prefabManager));
}
@Override
public void clear() {
store.clear();
nextEntityId = 1;
freedIds.clear();
entityCache.clear();
}
@Override
public EntityRef create() {
if (!freedIds.isEmpty()) {
createEntityRef(freedIds.removeAt(freedIds.size() - 1));
}
if (nextEntityId == NULL_ID) nextEntityId++;
return createEntityRef(nextEntityId++);
}
@Override
public EntityRef create(String prefabName) {
if (prefabName != null && !prefabName.isEmpty()) {
Prefab prefab = prefabManager.getPrefab(prefabName);
if (prefab == null) {
logger.log(Level.WARNING, "Unable to instantiate unknown prefab: \"" + prefabName + "\"");
return EntityRef.NULL;
}
return create(prefab);
}
return create();
}
@Override
public EntityRef create(Prefab prefab) {
EntityRef result = create();
if (prefab != null) {
for (Component component : prefab.listComponents()) {
result.addComponent(componentLibrary.copy(component));
}
result.addComponent(new EntityInfoComponent(prefab.getName()));
}
return result;
}
@Override
public int getComponentCount(Class<? extends Component> componentClass) {
return store.getComponentCount(componentClass);
}
@Override
public ComponentLibrary getComponentLibrary() {
return componentLibrary;
}
@Override
public EventSystem getEventSystem() {
return eventSystem;
}
@Override
public void setEventSystem(EventSystem eventSystem) {
this.eventSystem = eventSystem;
}
@Override
public PrefabManager getPrefabManager() {
return prefabManager;
}
@Override
public <T extends Component> Iterable<Map.Entry<EntityRef, T>> iterateComponents(Class<T> componentClass) {
TIntObjectIterator<T> iterator = store.componentIterator(componentClass);
if (iterator != null) {
List<Map.Entry<EntityRef, T>> list = new ArrayList<Map.Entry<EntityRef, T>>();
while (iterator.hasNext()) {
iterator.advance();
list.add(new EntityEntry<T>(createEntityRef(iterator.key()), iterator.value()));
}
return list;
}
return NullIterator.newInstance();
}
public Iterable<EntityRef> iteratorEntities() {
return new Iterable<EntityRef>() {
public Iterator<EntityRef> iterator() {
return new EntityIterator(store.entityIdIterator());
}
};
}
public Iterable<EntityRef> iteratorEntities(Class<? extends Component>... componentClasses) {
if (componentClasses.length == 0) {
return iteratorEntities();
}
TIntList idList = new TIntArrayList();
TIntObjectIterator<? extends Component> primeIterator = store.componentIterator(componentClasses[0]);
if (primeIterator == null) {
return NullIterator.newInstance();
}
while (primeIterator.hasNext()) {
primeIterator.advance();
int id = primeIterator.key();
boolean discard = false;
for (int i = 1; i < componentClasses.length; ++i) {
if (store.get(id, componentClasses[i]) == null) {
discard = true;
break;
}
}
if (!discard) {
idList.add(primeIterator.key());
}
}
return new EntityIterable(idList);
}
boolean hasComponent(int entityId, Class<? extends Component> componentClass) {
return store.get(entityId, componentClass) != null;
}
Iterable<Component> iterateComponents(int entityId) {
return store.iterateComponents(entityId);
}
void destroy(int entityId) {
EntityRef ref = createEntityRef(entityId);
if (eventSystem != null) {
eventSystem.send(ref, RemovedComponentEvent.newInstance());
}
entityCache.remove(ref);
freedIds.add(entityId);
if (ref instanceof PojoEntityRef) {
((PojoEntityRef) ref).invalidate();
}
store.remove(entityId);
}
<T extends Component> T getComponent(int entityId, Class<T> componentClass) {
return store.get(entityId, componentClass);
}
<T extends Component> T addComponent(int entityId, T component) {
Component oldComponent = store.put(entityId, component);
if (eventSystem != null) {
if (oldComponent == null) {
eventSystem.send(createEntityRef(entityId), AddComponentEvent.newInstance(), component);
} else {
eventSystem.send(createEntityRef(entityId), ChangedComponentEvent.newInstance(), component);
}
}
return component;
}
void removeComponent(int entityId, Class<? extends Component> componentClass) {
Component component = store.get(entityId, componentClass);
if (component != null) {
if (eventSystem != null) {
eventSystem.send(createEntityRef(entityId), RemovedComponentEvent.newInstance(), component);
}
store.remove(entityId, componentClass);
}
}
void saveComponent(int entityId, Component component) {
if (eventSystem != null) {
eventSystem.send(createEntityRef(entityId), ChangedComponentEvent.newInstance(), component);
}
}
public EntityRef createEntityRefWithId(int id) {
if (!freedIds.contains(id)) {
return createEntityRef(id);
}
return EntityRef.NULL;
}
private EntityRef createEntityRef(int entityId) {
if (entityId == NULL_ID) {
return EntityRef.NULL;
}
PojoEntityRef newRef = new PojoEntityRef(this, entityId);
PojoEntityRef existing = entityCache.get(newRef);
if (existing != null) {
return existing;
}
entityCache.put(newRef, newRef);
return newRef;
}
public int getNextId() {
return nextEntityId;
}
public void setNextId(int id) {
nextEntityId = id;
}
public TIntList getFreedIds() {
return freedIds;
}
private static class EntityEntry<T> implements Map.Entry<EntityRef, T> {
private EntityRef key;
private T value;
public EntityEntry(EntityRef ref, T value) {
this.key = ref;
this.value = value;
}
public EntityRef getKey() {
return key;
}
public T getValue() {
return value;
}
public T setValue(T value) {
throw new UnsupportedOperationException();
}
}
private class EntityIterable implements Iterable<EntityRef> {
private TIntList list;
public EntityIterable(TIntList list) {
this.list = list;
}
public Iterator<EntityRef> iterator() {
return new EntityIterator(list.iterator());
}
}
private class EntityIterator implements Iterator<EntityRef> {
private TIntIterator idIterator;
public EntityIterator(TIntIterator idIterator) {
this.idIterator = idIterator;
}
public boolean hasNext() {
return idIterator.hasNext();
}
public EntityRef next() {
return createEntityRef(idIterator.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}