package electrosphere.entity;

import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Vector3d;
import org.joml.Vector3f;

/**
 *
 * @author satellite
 */
public class EntityManager {
    
    
    static ConcurrentHashMap entityIdMap = new ConcurrentHashMap();
    static CopyOnWriteArrayList<Entity> entityList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> drawableList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> moveableList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> lightList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> uiList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> itemList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> boneAttachedList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> attackerList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> creatureList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> lifeStateList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> particleList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> gravityList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> collidableList = new CopyOnWriteArrayList();
    static CopyOnWriteArrayList<Entity> targetableList = new CopyOnWriteArrayList();
    
    public EntityManager(){
        
    }
    
    public void registerEntity(Entity e){
        entityIdMap.put(e.getId(), e);
        entityList.add(e);
    }
    
    public void registerDrawableEntity(Entity e){
        drawableList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getDrawable(){
        return drawableList;
    }
    
    public void registerMoveableEntity(Entity e){
        moveableList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getMoveable(){
        return moveableList;
    }
    
    public void registerLightEntity(Entity e){
        lightList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getLights(){
        return lightList;
    }
    
    public void registerUIEntity(Entity e){
        uiList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getUIElements(){
        return uiList;
    }
    
    public void registerItemEntity(Entity e){
        itemList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getItemEntities(){
        return itemList;
    }
    
    public void registerBoneAttachedEntity(Entity e){
        boneAttachedList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getBoneAttachedEntities(){
        return boneAttachedList;
    }
    
    public void registerAttackerEntity(Entity e){
        attackerList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getAttackerEntities(){
        return attackerList;
    }
    
    public void registerCreatureEntity(Entity e){
        creatureList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getCreatureEntities(){
        return creatureList;
    }
    
    public void registerLifeStateEntity(Entity e){
        lifeStateList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getLifeStateEntities(){
        return lifeStateList;
    }
    
    public void registerParticle(Entity e){
        particleList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getParticles(){
        return particleList;
    }
    
    public void registerGravityEntity(Entity e){
        gravityList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getGravityEntities(){
        return gravityList;
    }
    
    public void registerCollidableEntity(Entity e){
        collidableList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getCollidables(){
        return collidableList;
    }
    
    public void registerTargetableEntity(Entity e){
        targetableList.add(e);
    }
    
    public CopyOnWriteArrayList<Entity> getTargetables(){
        return targetableList;
    }
    
    public void deregisterEntity(Entity e){
        if(lightList.contains(e)){
            lightList.remove(e);
        }
        if(moveableList.contains(e)){
            moveableList.remove(e);
        }
        if(drawableList.contains(e)){
            drawableList.remove(e);
            EntityUtils.cleanUpDrawableEntity(e);
        }
        if(entityList.contains(e)){
            entityList.remove(e);
        }
        if(uiList.contains(e)){
            uiList.remove(e);
        }
        if(itemList.contains(e)){
            itemList.remove(e);
        }
        if(attackerList.contains(e)){
            attackerList.remove(e);
        }
        if(creatureList.contains(e)){
            creatureList.remove(e);
        }
        if(lifeStateList.contains(e)){
            lifeStateList.remove(e);
        }
        if(particleList.contains(e)){
            particleList.remove(e);
        }
        if(gravityList.contains(e)){
            gravityList.remove(e);
        }
        if(collidableList.contains(e)){
            collidableList.remove(e);
        }
        if(targetableList.contains(e)){
            targetableList.remove(e);
        }
    }
    
    public void recursiveDeregister(Entity target){
        if(AttachUtils.hasChildren(target)){
            List<Entity> childrenList = AttachUtils.getChildrenList(target);
            for(Entity currentChild : childrenList){
                recursiveDeregister(currentChild);
            }
        }
        deregisterEntity(target);
    }
    
    public void recursiveHide(Entity target){
        if(AttachUtils.hasChildren(target)){
            List<Entity> childrenList = AttachUtils.getChildrenList(target);
            for(Entity currentChild : childrenList){
                recursiveHide(currentChild);
            }
        }
        target.putData(EntityDataStrings.DATA_STRING_DRAW, false);
    }
    
    public void recursiveShow(Entity target){
        if(AttachUtils.hasChildren(target)){
            List<Entity> childrenList = AttachUtils.getChildrenList(target);
            for(Entity currentChild : childrenList){
                recursiveShow(currentChild);
            }
        }
        target.putData(EntityDataStrings.DATA_STRING_DRAW, true);
    }
    
    public void overrideEntityId(Entity e, int id){
        LoggerInterface.loggerGameLogic.DEBUG("Overriding entity ID " + e.getId() + " => " + id);
        if(entityIdMap.contains(e.getId())){
            entityIdMap.remove(e.getId());
        }
        e.setId(id);
        entityIdMap.put(e.getId(), e);
    }
    
    public Entity getEntityFromId(int id){
        return (Entity)entityIdMap.get(id);
    }
    
    public void clearOutOfBoundsEntities(){
        if(Globals.commonWorldData != null && Globals.playerCharacter != null && Globals.clientPlayerData != null){
            Vector3d playerCharacterPos = EntityUtils.getPosition(Globals.playerCharacter);
            int playerCharacterWorldX = Globals.commonWorldData.convertRealToWorld(playerCharacterPos.x);
            int playerCharacterWorldY = Globals.commonWorldData.convertRealToWorld(playerCharacterPos.z);
            if(playerCharacterWorldX != Globals.clientPlayerData.getWorldPositionX() || playerCharacterWorldY != Globals.clientPlayerData.getWorldPositionY()){
                for(Entity entity : entityList){
                    if(entity.getDataKeys().contains(EntityDataStrings.TERRAIN_IS_TERRAIN) || entity.getDataKeys().contains(EntityDataStrings.ATTACH_PARENT) || entity.getDataKeys().contains(EntityDataStrings.COLLISION_ENTITY_PARENT)){

                    } else {
                        Vector3d position = EntityUtils.getPosition(entity);
                        //common world data is initialized with the collision data
                        //if this is null then the engine hasn't fully started up yet
                        if(position != null){
                            int worldX = Globals.commonWorldData.convertRealToWorld(position.x);
                            int worldY = Globals.commonWorldData.convertRealToWorld(position.z);
                            if(!Globals.drawCellManager.coordsInPhysicsSpace(worldX, worldY)){
                                //if we're running the server we need to just hide the entity
                                //otherwise delete it
                                if(Globals.RUN_SERVER && Globals.RUN_CLIENT){
                                    recursiveHide(entity);
                                } else {
                                    recursiveDeregister(entity);
                                }
                            } else {
                                //if the entity is within range, we're running server,
                                //and it's not set to visible, make it visible
                                if(Globals.RUN_SERVER && Globals.RUN_CLIENT){
                                    recursiveShow(entity);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
}
