package electrosphere.server.datacell;

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

import electrosphere.collision.CollisionEngine;
import electrosphere.collision.CollisionWorldData;
import electrosphere.collision.hitbox.HitboxManager;
import electrosphere.entity.Entity;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.net.server.player.Player;
import electrosphere.server.content.ServerContentManager;
import electrosphere.server.datacell.physics.ServerHitboxResolutionCallback;

/**
 * Manages all realms for the engine. Should be a singleton
 */
public class RealmManager {

    //All realms in this manager
    Set<Realm> realms = new CopyOnWriteArraySet<Realm>();
    //Map of entities to the realm the entity is in
    Map<Entity,Realm> entityToRealmMap = new ConcurrentHashMap<Entity,Realm>();
    //Map of player to the realm the player is in
    Map<Player,Realm> playerToRealmMap = new ConcurrentHashMap<Player,Realm>();

    /**
     * Constructor
     */
    public RealmManager(){

    }


    /**
     * Creates a realm
     * @return The realm
     */
    public Realm createRealm(){
        return new Realm(new ServerWorldData(), new CollisionEngine(), new HitboxManager(new ServerHitboxResolutionCallback()), ServerContentManager.createServerContentManager(false));
    }

    /**
     * Creates a realm that uses a gridded layout (ie an array of cells in 3d space)
     * @return The realm
     */
    public Realm createGriddedRealm(ServerWorldData serverWorldData, ServerContentManager serverContentManager){
        //create collision engine
        CollisionEngine collisionEngine = new CollisionEngine();
        collisionEngine.setCollisionWorldData(new CollisionWorldData(serverWorldData));
        //create realm
        Realm realm = new Realm(serverWorldData, collisionEngine, new HitboxManager(new ServerHitboxResolutionCallback()), serverContentManager);
        //create function classes
        GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm);
        EntityDataCellMapper entityDataCellMapper = new EntityDataCellMapper();
        //add function classes to realm
        realm.setDataCellManager(griddedDataCellManager);
        realm.setEntityDataCellMapper(entityDataCellMapper);
        //register within the manager
        realms.add(realm);
        return realm;
    }

    /**
     * Maps an entity to a realm
     * @param entity The entity
     * @param realm The realm
     */
    public void mapEntityToRealm(Entity entity, Realm realm){
        entityToRealmMap.put(entity, realm);
    }

    /**
     * Removes the entity from tracking in this realm manager
     * @param entity The entity to remove
     */
    public void removeEntity(Entity entity){
        entityToRealmMap.remove(entity);
    }

    /**
     * Gets the realm an entity is inside of
     * @param entity The entity
     * @return The realm, or null if the entity is not inside a realm
     */
    public Realm getEntityRealm(Entity entity){
        return entityToRealmMap.get(entity);
    }

    /**
     * Gets the set containing all realms in the manager
     * @return The set containing all realms in the manager
     */
    public Set<Realm> getRealms(){
        return realms;
    }


    /**
     * Simulates all realms in this manager
     */
    public void simulate(){
        for(Realm realm : realms){
            realm.simulate();
        }
    }

    //TODO: !!URGENT!! come up with some mechanism to enforce this actually being called every time a player is added to a server data cell
    /**
     * Adds a player to a realm
     * @param player The player
     * @param realm The realm
     */
    public void setPlayerRealm(Player player, Realm realm){
        playerToRealmMap.put(player, realm);
    }

    /**
     * Gets the realm of a given player
     * @param player The player
     * @return The realm
     */
    public Realm getPlayerRealm(Player player){
        return playerToRealmMap.get(player);
    }

    /**
     * Saves all cells in all realms in the realm manager
     * @param saveName The name of the save
     */
    public void save(String saveName){
        for(Realm realm : realms){
            realm.save(saveName);
        }
    }

    
}
