package electrosphere.client.terrain.cache;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.joml.Vector3i;


/**
 * Acts as a cache in front of terrain model to streamline receiving chunks
 */
public class ClientTerrainCache {
    
    //cache capacity
    int cacheSize;
    //the map of chunk key -> chunk data
    Map<String,ChunkData> cacheMap = new ConcurrentHashMap<String,ChunkData>();
    //the list of keys in the cache
    List<String> cacheList = new CopyOnWriteArrayList<String>();
    //A map of chunk to its world position
    Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>();
    
    /**
     * Constructor
     * @param cacheSize The capacity of the cache
     */
    public ClientTerrainCache(int cacheSize){
        this.cacheSize = cacheSize;
    }
    
    /**
     * Adds a chunk data to the terrain cache
     * @param worldX The x world position
     * @param worldY The y world position
     * @param worldZ The z world position
     * @param chunkData The chunk data to add at the specified positions
     */
    public void addChunkDataToCache(int worldX, int worldY, int worldZ, ChunkData chunkData){
        cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
        chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
        while(cacheList.size() > cacheSize){
            String currentChunk = cacheList.remove(0);
            cacheMap.remove(currentChunk);
        }
    }
    
    
    /**
     * Generates a key for the cache based on the position provided
     * @param worldX The x world position
     * @param worldY The y world position
     * @param worldZ The z world position
     * @return The cache key
     */
    public String getKey(int worldX, int worldY, int worldZ){
        return worldX + "_" + worldY + "_" + worldZ;
    }
    
    /**
     * Checks whether the cache contains chunk data at a given world point
     * @param worldX The x world position
     * @param worldY The y world position
     * @param worldZ The z world position
     * @return True if the cache contains chunk data at the specified point, false otherwise
     */
    public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
        return cacheMap.containsKey(getKey(worldX,worldY,worldZ));
    }
    
    
    
    
    
    /**
     * Gets chunk data at the given world point
     * @param worldX The x world position
     * @param worldY The y world position
     * @param worldZ The z world position
     * @return The chunk data if it exists, null otherwise
     */
    public ChunkData getSubChunkDataAtPoint(int worldX, int worldY, int worldZ){
        return cacheMap.get(getKey(worldX,worldY,worldZ));
    }

    /**
     * Gets the list of all chunks in the cache
     * @return The list of all chunks in the cache
     */
    public Collection<ChunkData> getAllChunks(){
        return this.cacheMap.values();
    }

    /**
     * Gets the world position of a chunk
     * @param chunk The chunk
     * @return The world position of the chunk
     */
    public Vector3i getChunkPosition(ChunkData chunk){
        return chunkPositionMap.get(chunk);
    }
    
}
