package electrosphere.entity.types.foliage;

import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.client.ambientaudio.ClientAmbientAudioTree;
import electrosphere.entity.types.tree.ProceduralTree;
import electrosphere.game.data.foliage.type.AmbientAudio;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player;
import electrosphere.renderer.actor.ActorUtils;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.ServerEntityTagUtils;

import org.joml.Vector3d;

/**
 * Utilities for generating foliage
 */
public class FoliageUtils {

    //the entity type value
    public static final int ENTITY_TYPE_FOLIAGE = 3;
    
    /**
     * Spawns a basic foliage object
     * @param type The type of foliage object
     * @return The entity for the foliage
     */
    public static Entity spawnBasicFoliage(String type, long seed){
        FoliageType rawType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(type);
        Entity rVal;
        if(rawType.getTreeModel()!=null){
            rVal = ProceduralTree.clientGenerateProceduralTree(type, 0);
        } else {
            rVal = EntityCreationUtils.createClientSpatialEntity();
            EntityCreationUtils.makeEntityDrawable(rVal, rawType.getModelPath());
        }

        for(String token : rawType.getTokens()){
            switch(token){
                case "BLENDER_ROTATION":
                    ActorUtils.applyBlenderRotation(rVal);
                    break;
                case "PARTICLE_SPAWNER":
                    
                    break;
            }
        }
        //idle tree & generic stuff all creatures have
//        rVal.putData(EntityDataStrings.IDLE_TREE, new IdleTree(rVal));
//        Globals.entityManager.registerCreatureEntity(rVal);
        rVal.putData(EntityDataStrings.FOLIAGE_IS_FOLIAGE, true);
        rVal.putData(EntityDataStrings.FOLIAGE_TYPE, type);
        return rVal;
    }

    
    /**
     * Spawns a tree foliage on the server
     * @param realm The realm to spawn on
     * @param position The position of the tree
     * @param type the type of tree
     * @param seed the seed for the tree
     * @return the tree entity
     */
    public static Entity serverSpawnTreeFoliage(Realm realm, Vector3d position, String type, long seed){
        FoliageType rawType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(type);
        Entity rVal = ProceduralTree.serverGenerateProceduralTree(realm, position, rawType, seed);

        for(String token : rawType.getTokens()){
            switch(token){
                case "BLENDER_ROTATION":
                    ActorUtils.applyBlenderRotation(rVal);
                    break;
                case "PARTICLE_SPAWNER":
                    
                    break;
            }
        }
        //audio
        if(rawType.getAmbientAudio()!=null){
            AmbientAudio ambientAudio = rawType.getAmbientAudio();
            ClientAmbientAudioTree.attachTree(rVal, ambientAudio);
        }
        //
        ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.FOLIAGE);
        rVal.putData(EntityDataStrings.FOLIAGE_IS_FOLIAGE, true);
        rVal.putData(EntityDataStrings.FOLIAGE_TYPE, rawType);
        rVal.putData(EntityDataStrings.FOLIAGE_SEED, seed);
        rVal.putData(EntityDataStrings.FOLIAGE_IS_SEEDED, true);
        rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);

        //position entity
        //this needs to be called at the end of this function.
        //Burried underneath this is function call to initialize a server side entity.
        //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
        //the server will not be able to synchronize it properly.
        ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);

        return rVal;
    }

    /**
     * Gets the seed for a given foliage item
     * @param entity The entity
     * @return The seed
     */
    public static long getFoliageSeed(Entity entity){
        return (long)entity.getData(EntityDataStrings.FOLIAGE_SEED);
    }

    /**
     * Gets the type of foliage
     * @param entity the entity
     * @return The type
     */
    public static FoliageType getFoliageType(Entity entity){
        return (FoliageType)entity.getData(EntityDataStrings.FOLIAGE_TYPE);
    }

    /**
     * Gets whether the entity is foliage or not
     * @param entity The entity
     * @return true if is foliage, false otherwise
     */
    public static boolean isFoliage(Entity entity){
        return entity.getData(EntityDataStrings.FOLIAGE_IS_FOLIAGE)!=null;
    }

    /**
     * Gets whether the entity has a foliage seed or not
     * @param entity The entity
     * @return true if is has seed, false otherwise
     */
    public static boolean hasSeed(Entity entity){
        return entity.getData(EntityDataStrings.FOLIAGE_IS_SEEDED)!=null;
    }


    /**
     * Sends a given foliage entity to a given player
     * @param player The player
     * @param foliage The foliage entity
     */
    public static void sendFoliageToPlayer(Player player, Entity foliage){
        int id = foliage.getId();
        FoliageType type = FoliageUtils.getFoliageType(foliage);
        Vector3d position = EntityUtils.getPosition(foliage);
        if(FoliageUtils.hasSeed(foliage)){
            long seed = FoliageUtils.getFoliageSeed(foliage);
            NetworkMessage message = EntityMessage.constructSpawnFoliageSeedMessage(
                id,
                type.getName(),
                seed,
                position.x,
                position.y,
                position.z
            );
            player.addMessage(message);
        } else {
            //still needs to be implemented with new network message
            throw new UnsupportedOperationException("Tried to spawn a foliage object that doesn't have a seed.");
        }
    }
    
    
}
