package net.quasardb.qdb;

import java.lang.AutoCloseable;
import net.quasardb.qdb.jni.*;

/**
 * A batch containing a list of operation.
 *
 * Batches are used to reduce the number of requests by performing several operations in one request. 
 */
public final class QdbBatch implements AutoCloseable {
    private final QdbSession session;
    private BatchOpsVec operations;
    private boolean hasRun;
    private int successCount;

    // Protected constructor. Call  QdbCluster.createBatch() to create a batch.
    protected QdbBatch(QdbSession session) {
        this.session = session;
        operations = new BatchOpsVec();
    }

    /**
     * Add blob operations to the batch.
     *
     * A call to this method is usually followed by a call to an operation.
     * For example:
     * {@code
     * QdbFuture<ByteBuffer> result = myBatch.blob("myBlob").get();
     * }
     *
     * @param alias The alias of the blob you want to add operations for.
     * @return A handle to a virtual blob on with to perform the operation.
     * @throws QdbBatchClosedException If close() has been called.
     * @throws QdbBatchAlreadyRunException If the run() has been called.
     */
    public QdbBatchBlob blob(String alias) {
        throwIfClosed();
        throwIfHasRun();
        return new QdbBatchBlob(this, alias);
    }

    /**
     * Executes all operations in the batch.
     *
     * A batch can only be run once.
     * Once a batch is run, most method will throw a QdbBatchAlreadyRunException.
     *
     * @throws QdbBatchClosedException If close() has been called.
     * @throws QdbBatchAlreadyRunException If the run() has been called.
     */
    public void run() {
        throwIfClosed();
        throwIfHasRun();

        successCount = (int)qdb.run_batch2(session.handle(), operations);
        hasRun = true;
    }

    /**
     * If you forgot to call close() we have you covered.
     */
    @Override
    protected void finalize() throws Throwable {
        this.close();
        super.finalize();
    }

    /**
     * Release the memory allocated by quasardb.
     *
     * Once this method has been called, most other methods will throw QdbBatchClosedException.
     */
    public void close() {
        if (operations != null) {
            qdb.free_operations(session.handle(), operations);
            operations = null;
        }
    }

    /**
     * Check if all operations executed successfully.
     *
     * @return true if all operations succeeded, false if any operation failed.
     * @throws QdbBatchClosedException If close() has been called.
     * @throws QdbBatchNotRunException If run() has not been called.
     */
    public boolean success() {
        throwIfClosed();
        throwIfNotRun();
        return operations.size() == successCount;
    }

    /**
     * Gets the total number of operations in the batch
     *
     * @return The number of operations that the batch contains.
     * @throws QdbBatchClosedException If close() has been called.
     */
    public int operationCount() {
        throwIfClosed();
        return (int)operations.size();
    }

    /**
     * Gets the number of successful operations
     *
     * @return The number of successful operations
     * @throws QdbBatchClosedException If close() has been called.
     * @throws QdbBatchNotRunException If run() has not been called.
     */
    public int successCount() {
        throwIfClosed();
        throwIfNotRun();
        return successCount;
    }

    /**
     * Checks if close() has been called.
     *
     * @return true if batch has been closed, false if not
     */
    public boolean isClosed() {
        return operations == null;
    }

    protected int addOperation(qdb_operation_t op) {
        throwIfClosed();
        throwIfHasRun();
        int index = (int)operations.size();
        operations.push_back(op);
        return index;
    }

    protected qdb_operation_t getOperation(int index) {
        throwIfClosed();
        return operations.get(index);
    }

    protected boolean hasRun() {
        throwIfClosed();
        return hasRun;
    }

    private void throwIfClosed() {
        if (isClosed())
            throw new QdbBatchClosedException();
    }

    private void throwIfHasRun() {
        if (hasRun)
            throw new QdbBatchAlreadyRunException();
    }

    private void throwIfNotRun() {
        if (!hasRun)
            throw new QdbBatchNotRunException();
    }
}

@@ -52,16 +52,45 @@
 seable;%0A
+import java.util.LinkedList;%0A
 import n
@@ -112,16 +112,16 @@
 .jni.*;%0A
-
 %0A/**%0A * 
@@ -359,24 +359,116 @@
 on session;%0A
+    private LinkedList%3Cqdb_operation_t%3E queuedOperations; // %3C- to keep O(1) insertion time%0A
     private 
@@ -479,17 +479,25 @@
 hOpsVec 
-o
+executedO
 peration
@@ -498,16 +498,57 @@
 rations;
+               // %3C- to store the results
 %0A    pri
@@ -756,33 +756,103 @@
 ession;%0A        
-o
+queuedOperations = new LinkedList%3Cqdb_operation_t%3E();%0A        executedO
 perations = new 
@@ -1978,16 +1978,196 @@
 Run();%0A%0A
+        executedOperations.reserve(queuedOperations.size());%0A        while (!queuedOperations.isEmpty())%0A            executedOperations.push_back(queuedOperations.removeFirst());%0A%0A
         
@@ -2211,33 +2211,41 @@
 ssion.handle(), 
-o
+executedO
 perations);%0A    
@@ -2232,32 +2232,33 @@
 tedOperations);%0A
+%0A
         hasRun =
@@ -2669,17 +2669,25 @@
     if (
-o
+executedO
 peration
@@ -2749,17 +2749,25 @@
 ndle(), 
-o
+executedO
 peration
@@ -2774,33 +2774,41 @@
 s);%0A            
-o
+executedO
 perations = null
@@ -3207,33 +3207,41 @@
 %0A        return 
-o
+executedO
 perations.size()
@@ -3550,22 +3550,65 @@
  return 
-(int)o
+hasRun ? (int)executedOperations.size() : queuedO
 peration
@@ -4163,33 +4163,41 @@
 %0A        return 
-o
+executedO
 perations == nul
@@ -4333,14 +4333,15 @@
 x = 
-(int)o
+queuedO
 pera
@@ -4362,17 +4362,23 @@
         
-o
+queuedO
 peration
@@ -4379,25 +4379,23 @@
 rations.
-push_back
+addLast
 (op);%0A  
@@ -4517,17 +4517,64 @@
  return 
-o
+hasRun ? executedOperations.get(index) : queuedO
 peration

Refactor QdbBatch.addOperation() to ensure O(1) complexity