package com.thimbleware.jmemcached.storage.mmap;

import com.thimbleware.jmemcached.storage.bytebuffer.ByteBufferBlockStore;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import static java.nio.channels.FileChannel.MapMode.PRIVATE;

/**
 * Memory mapped block storage mechanism with a free-list maintained by TreeMap
 *
 * Allows memory for storage to be mapped outside of the VM's main memory, and outside the purvey
 * of the GC.
 *
 * Should offer O(Log(N)) search and free of blocks.
 */
public final class MemoryMappedBlockStore extends ByteBufferBlockStore {

    private RandomAccessFile fileStorage;
    private String fileName;

    /**
     * Construct a new memory mapped block storage against a filename, with a certain size
     * and block size.
     * @param maxBytes the number of bytes to allocate in the file
     * @param fileName the filename to use
     * @param blockSizeBytes the size of a block in the store
     * @throws java.io.IOException thrown on failure to open the store or map the file
     */
    public MemoryMappedBlockStore(long maxBytes, String fileName, int blockSizeBytes) throws IOException {
        super();
        storageBuffer = getMemoryMappedFileStorage(maxBytes, fileName);
        initialize(storageBuffer.capacity(), blockSizeBytes);
    }

    private MappedByteBuffer getMemoryMappedFileStorage(long maxBytes, String fileName) throws IOException {
        // open the file for read-write
        this.fileName = fileName;
        fileStorage = new RandomAccessFile(fileName, "rw");
        fileStorage.seek(maxBytes);
        fileStorage.getChannel().map(PRIVATE, 0, maxBytes);

        return fileStorage.getChannel().map(PRIVATE, 0, maxBytes);
    }

    @Override
    protected void freeResources() throws IOException {
        super.freeResources();

        // close the actual file
        fileStorage.close();

        // delete the file; it is no longer of any use
        new File(fileName).delete();

        fileStorage = null;
    }


}