/**
 * 
 */
package org.forkalsrud.album.db;

import java.nio.charset.Charset;

import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

/**
 * @author knut
 * 
 */
public class MovieDatabase extends TupleBinding<Chunk> {

    private static Charset UTF8 = Charset.forName("utf-8");
    
    private Environment environment;
    private Database db;

    public MovieDatabase(Environment environment) {

        this.environment = environment;

        String dbname = "movies";

        DatabaseConfig databaseConfig = new DatabaseConfig();
        databaseConfig.setAllowCreate(true);
        databaseConfig.setTransactional(true);
        // perform other database configurations
        this.db = environment.openDatabase(null, dbname, databaseConfig);
    }

    public void close() {
        db.close();
    }
    
    public long size() {
        return db.count();
    }

    public Chunk load(String key, int seq) {

        DatabaseEntry data = new DatabaseEntry();
        Transaction txn = environment.beginTransaction(null, null);
        OperationStatus status = db.get(txn, key(key, seq), data, null);
        txn.commitNoSync();
        if (OperationStatus.SUCCESS.equals(status)) {
            return entryToObject(data);
        } else {
            return null;
        }
    }

    public void store(String key, int seq, Chunk chunk) {

        DatabaseEntry data = new DatabaseEntry();
        objectToEntry(chunk, data);
        DatabaseEntry binKey = key(key, seq);
        Transaction txn = environment.beginTransaction(null, null);
        db.delete(txn, binKey);
        db.put(txn, binKey, data);
        txn.commitSync();
    }


    public void delete(String key) {

        int seq = 0;
        OperationStatus status = OperationStatus.SUCCESS;
        Transaction txn = environment.beginTransaction(null, null);
        while (OperationStatus.SUCCESS.equals(status)) {
            DatabaseEntry binKey = key(key, seq++);
            status = db.delete(txn, binKey);
        }
        txn.commitSync();
    }


    private DatabaseEntry key(String key, int seq) {
        DatabaseEntry returnValue = new DatabaseEntry();
        returnValue.setData((key + "#" + seq).getBytes(UTF8));
        return returnValue;
    }


    @Override
    public Chunk entryToObject(TupleInput in) {

        int version = in.readInt();
        if (version != 1 && version != 2 && version != 3) {
            throw new RuntimeException("I only understand versions 1, 2 and 3");
        }
        long ts = version >= 3 ? in.readLong() : 0;
        int lobLen = in.readInt();
        int metaLen = version >= 2 ? in.readInt() : 0;
        Chunk chunk = new Chunk(ts, lobLen, metaLen);
        in.read(chunk.bits, 0, lobLen);
        if (version >= 2) {
            in.read(chunk.meta, 0, metaLen);
        }
        return chunk;
    }


    @Override
    public void objectToEntry(Chunk chunk, TupleOutput out) {

        out.writeInt(3);  // version 3
        out.writeLong(chunk.timestamp);
        out.writeInt(chunk.bits.length);
        out.writeInt(chunk.meta.length);
        out.write(chunk.bits);
        out.write(chunk.meta);
    }

}
