package electrosphere.entity;

import electrosphere.engine.Globals;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorUtils;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.poseactor.PoseActor;
import electrosphere.server.poseactor.PoseActorUtils;
import electrosphere.util.math.MathUtils;

import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;

/**
 * Utilties for dealing with entities
 */
public class EntityUtils {
    
    public static Vector3d getPosition(Entity e){
        return (Vector3d)e.getData(EntityDataStrings.DATA_STRING_POSITION);
    }
    
    public static Quaterniond getRotation(Entity e){
        return (Quaterniond)e.getData(EntityDataStrings.DATA_STRING_ROTATION);
    }
    
    public static Vector3f getScale(Entity e){
        return (Vector3f)e.getData(EntityDataStrings.DATA_STRING_SCALE);
    }
    
    public static String getModelPath(Entity e){
        return (String)e.getData(EntityDataStrings.DATA_STRING_MODEL_PATH);
    }
    
    /**
     * Creates an entity with an associated actor
     * @param modelPath The model path of the model for the actor
     * @return The entity
     */
    protected static Entity spawnDrawableEntity(String modelPath){
        Entity rVal = new Entity();
        rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath));
//        rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
        rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
        rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
        Globals.clientScene.registerEntity(rVal);
        Globals.clientScene.registerEntityToTag(rVal, EntityTags.DRAWABLE);
        return rVal;
    }
    
    /**
     * Creates an entity with an actor based on a model that already exists or is actively being loaded, not sure
     * @param modelPath The path to the model in asset manager
     * @return The entity
     */
    protected static Entity spawnDrawableEntityWithPreexistingModel(String modelPath){
        Entity rVal = new Entity();
        rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath));
        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, MathUtils.getOriginVector()));
        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
        rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
        rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
        Globals.clientScene.registerEntity(rVal);
        Globals.clientScene.registerEntityToTag(rVal, EntityTags.DRAWABLE);
        return rVal;
    }

    /**
     * Spawns an entity with a backing pose actor (server side presumably)
     * @param modelPath The model path to back the pose actor
     * @return The entity
     */
    protected static Entity spawnPoseableEntity(String modelPath){
        Entity rVal = new Entity();
        rVal.putData(EntityDataStrings.POSE_ACTOR, PoseActorUtils.createPoseActorFromModelPath(modelPath));
//        rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, modelPath);
        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
        rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true);
        rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
        return rVal;
    }
    
    protected static Entity spawnUIEntity(String modelPath){
        Entity rVal = new Entity();
        rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath));
        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, MathUtils.getOriginVector()));
        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
        rVal.putData(EntityDataStrings.DATA_STRING_UI_ELEMENT, true);
        Globals.clientScene.registerEntity(rVal);
        Globals.clientScene.registerEntityToTag(rVal, EntityTags.UI);
        return rVal;
    }

    /**
     * Spawns an entity that has a position in the world, but isn't necessarily drawable
     * @return the entity
     */
    protected static Entity spawnSpatialEntity(){
        Entity rVal = new Entity();
        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
        return rVal;
    }
    
    /**
     * Cleans up the entity and deregisters it from all tracking datastructures
     * @param e The entity to clean up
     */
    public static void cleanUpEntity(Entity e){
        //remove from client
        Globals.clientSceneWrapper.getScene().deregisterEntity(e);
        //remove from all server classes
        if(Globals.realmManager != null){
            Realm realm = Globals.realmManager.getEntityRealm(e);
            if(realm != null){
                //get data cell
                ServerDataCell dataCell = realm.getEntityDataCellMapper().getEntityDataCell(e);
                if(dataCell != null){
                    dataCell.getScene().deregisterEntity(e);
                }
                realm.getEntityDataCellMapper().ejectEntity(e);
            }
            Globals.realmManager.removeEntity(e);
        }
        EntityLookupUtils.removeEntity(e);
    }
    
    /**
     * Gets the actor on the entity
     * @param e The entity
     * @return The actor
     */
    public static Actor getActor(Entity e){
        return (Actor)e.getData(EntityDataStrings.DATA_STRING_ACTOR);
    }

    /**
     * Gets the pose actor on the entity
     * @param e The entity
     * @return the pose actor
     */
    public static PoseActor getPoseActor(Entity e){
        return (PoseActor)e.getData(EntityDataStrings.POSE_ACTOR);
    }
    
    public static void setVisible(Entity entity, boolean visible){
        entity.putData(EntityDataStrings.DATA_STRING_DRAW, visible);
    }

    public static void setDraw(Entity entity, boolean draw){
        entity.putData(EntityDataStrings.DATA_STRING_DRAW, draw);
    }

    public static boolean getDraw(Entity entity){
        return (boolean)entity.getData(EntityDataStrings.DATA_STRING_DRAW);
    }

    /**
     * Gets the type of the entity
     * @param entity The entity
     * @return The type
     */
    public static int getEntityType(Entity entity){
        return (int)entity.getData(EntityDataStrings.ENTITY_TYPE);
    }

    /**
     * Sets the entity type
     * @param entity the entity
     * @param type the type
     */
    public static void setEntityType(Entity entity, int type){
        entity.putData(EntityDataStrings.ENTITY_TYPE, type);
    }

    /**
     * Gets the subtype of this entity
     * @param entity The entity
     * @return The subtype
     */
    public static String getEntitySubtype(Entity entity){
        return (String)entity.getData(EntityDataStrings.ENTITY_SUBTYPE);
    }

    /**
     * Sets the subtype of this entity
     * @param entity The entity
     * @param subtype The subtype
     */
    public static void setEntitySubtype(Entity entity, String subtype){
        entity.putData(EntityDataStrings.ENTITY_SUBTYPE, subtype);
    }
    
}
