package electrosphere.entity;

import electrosphere.engine.Globals;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.logger.LoggerInterface;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * A game scene
 */
public class Scene {
    
    
    Map<Integer,Entity> entityIdMap = new ConcurrentHashMap<Integer,Entity>();
    Map<String,Set<Entity>> tagEntityMap = new ConcurrentHashMap<String,Set<Entity>>();
    List<Entity> entityList = new CopyOnWriteArrayList<Entity>();

    List<BehaviorTree> behaviorTreeList = new CopyOnWriteArrayList<BehaviorTree>();
    
    public Scene(){
        tagEntityMap.put(EntityTags.BONE_ATTACHED, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.COLLIDABLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.SPRINTABLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.MOVEABLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.ATTACKER, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.TARGETABLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.LIFE_STATE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.CREATURE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.UI, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.DRAWABLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.DRAW_INSTANCED, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.LIGHT, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.ITEM, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.GRAVITY, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.PARTICLE, new CopyOnWriteArraySet<Entity>());
        tagEntityMap.put(EntityTags.TRANSFORM_ATTACHED, new CopyOnWriteArraySet<Entity>());
    }
    
    /**
     * Registers an entity to the scene
     * @param e The entity to register
     */
    public void registerEntity(Entity e){
        if(!entityIdMap.containsKey(e.getId())){
            entityList.add(e);
        }
        entityIdMap.put(e.getId(), e);
    }

    /**
     * Registers an entity to a given tag
     * @param e The entity
     * @param tag The tag
     */
    public void registerEntityToTag(Entity e, String tag){
        if(tagEntityMap.containsKey(tag)){
            tagEntityMap.get(tag).add(e);
        } else {
            Set<Entity> newEntityList = new CopyOnWriteArraySet<Entity>();
            newEntityList.add(e);
            tagEntityMap.put(tag,newEntityList);
        }
    }

    /**
     * Gets all entities registered to a tag
     * @param tag The tag
     * @return A list of all entities with the tag, or null if no entities have been added to the tag yet
     */
    public Set<Entity> getEntitiesWithTag(String tag){
        return tagEntityMap.get(tag);
    }

    /**
     * Removes an entity from a tag
     * @param e The entity
     * @param tag The tag
     */
    public void removeEntityFromTag(Entity e, String tag){
        tagEntityMap.get(tag).remove(e);
    }
    
    /**
     * Deregisters an entity from an entity manager
     * @param e
     */
    public void deregisterEntity(Entity e){
        for(String key : tagEntityMap.keySet()){
            tagEntityMap.get(key).remove(e);
        }
        entityIdMap.remove(e.getId());
        entityList.remove(e);
    }
    
    /**
     * Recursively deregisters an entity and all entities attached via AttachUtils
     * @param target The top level entity to deregister
     */
    public void recursiveDeregister(Entity target){
        if(AttachUtils.hasChildren(target)){
            List<Entity> childrenList = AttachUtils.getChildrenList(target);
            for(Entity currentChild : childrenList){
                recursiveDeregister(currentChild);
            }
        }
        deregisterEntity(target);
    }
    
    /**
     * Gets an entity via its ID
     * @param id The id to search for
     * @return The entity with that ID
     */
    public Entity getEntityFromId(int id){
        return (Entity)entityIdMap.get(id);
    }

    /**
     * Registers a behavior tree to simulate each scene simulation frame
     * @param tree The behavior tree to register
     */
    public void registerBehaviorTree(BehaviorTree tree){
        behaviorTreeList.add(tree);
    }

    /**
     * Deregisters a behavior tree from the scene
     * @param tree The behavior tree to deregister
     */
    public void deregisterBehaviorTree(BehaviorTree tree){
        while(behaviorTreeList.contains(tree)){
            behaviorTreeList.remove(tree);
        }
    }

    /**
     * Simulates all behavior trees stored in the entity manager
     */
    public void simulateBehaviorTrees(float deltaTime){
        Globals.profiler.beginCpuSample("Scene.simulateBehaviorTrees");
        for(BehaviorTree tree : behaviorTreeList){
            tree.simulate(deltaTime);
        }
        Globals.profiler.endCpuSample();
    }

    /**
     * Gets the list of all entities in the scene
     * @return The list of all entities in the scene
     */
    public List<Entity> getEntityList(){
        return entityList;
    }

    /**
     * Describes the scene in log messages
     */
    public void describeScene(){
        LoggerInterface.loggerEngine.WARNING("Entities present in scene:");
        for(Entity entity : this.entityList){
            LoggerInterface.loggerEngine.WARNING(entity.getId() + "");
        }
    }
    
}
