package electrosphere.renderer.actor;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;

import electrosphere.engine.Globals;
import electrosphere.renderer.model.Mesh;
import electrosphere.renderer.model.Model;

/**
 * A per-actor object that contains blockers for individual parts of the base meshes of the actor.
 * IE, it will contain the information that says "don't draw the base model's feet, instead draw a pair of boots"
 */
public class ActorMeshMask {



    //The map of base mesh name -> blocked status
    //Ie "feet" -> true would mean that the base model feet should not be drawn
    Map<String,Boolean> meshBlockerList = new HashMap<String,Boolean>();
    //the list of meshes to draw that are stored in this object
    //ie this would contain a mesh for boots
    Map<String,Mesh> toDrawMesh = new HashMap<String,Mesh>();

    //lock for queueing blockers
    Semaphore queueLock = new Semaphore(1);
    List<MeshDrawQueueItem> queuedMeshes = new LinkedList<MeshDrawQueueItem>();
    List<MeshDrawQueueItem> queuedBlockers = new LinkedList<MeshDrawQueueItem>();
    


    public ActorMeshMask(){
        
    }

    public void queueMesh(String modelName, String meshName){
        queueLock.acquireUninterruptibly();
        queuedMeshes.add(new MeshDrawQueueItem(modelName, meshName));
        queueLock.release();
    }

    public void processMeshMaskQueue(){
        Model model;
        Mesh mesh;
        if(queueLock.tryAcquire()){
            if(queuedMeshes.size() > 0 || queuedBlockers.size() > 0){
                //process queued meshes
                List<MeshDrawQueueItem> toRemove = new LinkedList<MeshDrawQueueItem>();
                for(MeshDrawQueueItem item : queuedMeshes){
                    if((model = Globals.assetManager.fetchModel(item.getModelName())) != null){
                        if((mesh = model.getMesh(item.getMeshName())) != null){
                            toDrawMesh.put(mesh.getMeshName(),mesh);
                            toRemove.add(item);
                        }
                    }
                }
                //remove successfully processed
                for(MeshDrawQueueItem item : toRemove){
                    queuedMeshes.remove(item);
                }
                toRemove.clear();
                //process queued blockers
                for(MeshDrawQueueItem item : queuedBlockers){
                    if((model = Globals.assetManager.fetchModel(item.getModelName())) != null){
                        meshBlockerList.put(item.getMeshName(),true);
                        toRemove.add(item);
                    }
                }
                //remove successfully processed
                for(MeshDrawQueueItem item : toRemove){
                    queuedBlockers.remove(item);
                }
            }
            queueLock.release();
        }
    }

    public List<Mesh> getToDrawMeshes(){
        return toDrawMesh.values().stream().collect(Collectors.toList());
    }

    public void ejectMeshToDraw(String name){
        toDrawMesh.remove(name);
    }

    public boolean isEmpty(){
        return toDrawMesh.size() > 0;
    }

    /**
     * Removes a mesh from the list of extra (non-base model) meshes to draw
     * @param additionalMeshName the name of the mesh to remove
     */
    public void removeAdditionalMesh(String additionalMeshName){
        toDrawMesh.remove(additionalMeshName);
    }


    //
    //blocking interactions
    //
    public boolean containsMeshToDraw(String name){
        return toDrawMesh.containsKey(name);
    }

    public void blockMesh(String modelName, String meshName){
        queueLock.acquireUninterruptibly();
        queuedBlockers.add(new MeshDrawQueueItem(modelName, meshName));
        queueLock.release();
    }

    public boolean isBlockedMesh(String name){
        return meshBlockerList.containsKey(name);
    }

    public void unblockMesh(String name){
        meshBlockerList.remove(name);
    }

    public List<String> getBlockedMeshes(){
        return meshBlockerList.keySet().stream().collect(Collectors.toList());
    }



    /**
     * A queued mesh blocker.
     * When a creature equips an item that would block a mesh, the mesh may not have already been loaded into memory.
     * This represents an item in a queue that contains a mesh that should be eventually used to block a mesh on the parent actor.
     */
    class MeshDrawQueueItem {

        String modelName;
        String meshName;

        MeshDrawQueueItem(String modelName, String meshName){
            this.modelName = modelName;
            this.meshName = meshName;
        }


        public String getModelName(){
            return modelName;
        }

        public String getMeshName(){
            return meshName;
        }

    }
    

}
