   (:require [org.micromanager.mm :as mm])
   (:use [org.micromanager.mm :only (core)]))
 
-(def native-order (ByteOrder/nativeOrder))
+;; bucket brigade
 
-(defn byte-buffer
-  "Create a direct byte-buffer with native order."
-  [size-bytes]
-  (.. ByteBuffer (allocateDirect size-bytes)
-      (order native-order)))
+(defn bucket-node [input-queue function output-queue]
+  (future
+    (loop []
+      (let [item (.take input-queue)]
+        (if (= item input-queue)
+          (.put output-queue output-queue)
+          (do (.put output-queue (function item))
+              (recur)))))))
 
-(defn image-to-byte-buffer
-  "Store 16-bit image pixel array data in a
-   pre-existing byte buffer instance."
-  [pix buffer]
-  (.. buffer asShortBuffer (put pix))
-  buffer)
+(defn first-node [n function output-queue]
+  (future
+    (dotimes [i n]
+      (.put output-queue (function)))
+    (.put output-queue output-queue)))
+
+(defn last-node [input-queue function]
+  (loop []
+    (let [item (.take input-queue)]
+      (when (not= item input-queue)
+        (function item)
+        (recur)))))
+        
+
+(defn bucket-brigade [n & functions]
+  (let [first-queue (LinkedBlockingQueue.)
+        first-node (first-node n (first functions) first-queue)]
+    (loop [remaining-functions (rest functions) input-queue first-queue]
+      (let [f (first remaining-functions)]
+        (if-let [n (next remaining-functions)]
+          (let [output-queue (LinkedBlockingQueue.)]
+            (bucket-node input-queue f output-queue)
+            (recur n output-queue))
+          (last-node input-queue f))))))
+      
+;; acquiring and popping images
 
 (defn run-sequence-acquisition
   "Start a sequence acquisition and, optionally, wait for it."

             (when (core isBufferOverflowed)
               (throw (Exception. "Circular buffer overflowed."))))))

(defn single-thread-pop-test
  "Run a sequence acquisition and pop the images,
   inserting them into image-queue as they arrive."
  [num-images image-queue]
  (run-sequence-acquisition num-images false)
  (dotimes [i num-images]
    (when-let [image (pop-next-image)]
      (when image-queue
        (.put image-queue image)))))

(defn byte-buffer-queue
  "Run a sequence acquisition, convert the incoming
   images to direct byte buffers, and return references
   to the buffers in a queue."
  [num-images]
  (let [image-queue (LinkedBlockingQueue. 1)]
    (future (single-thread-pop-test num-images image-queue))
    (let [queue (LinkedBlockingQueue. 1)]
      (future (dotimes [i num-images]
                (let [img (.take image-queue)
                      n (count img)]
                  (.put queue
                        (image-to-byte-buffer
                          img
                          (byte-buffer (* n 2))))
                  )))
      queue)))

(defmacro dotimes-timed [bindings interval & body]
  `(let [t0# (System/currentTimeMillis)]
     (println '~(first bindings) "time (ms)")
     (dotimes ~bindings
       (when (zero? (mod ~(first bindings) ~interval))
         (println ~(first bindings) (- (System/currentTimeMillis) t0#)
                  (core getRemainingImageCount)))
       ~@body)))

(defn write-buf [channel buffer]
  (.write channel buffer)
  )

(defn acquire-and-save
  "Simulate running a sequence acquisition, and save the
   images to a file using the direct byte buffer method."
  [num-images]
    (let [filename (str "D:/AcquisitionData/test" (rand-int 100000) ".dat")
          file (RandomAccessFile. filename "rw")
          channel (.getChannel file)]
      (try
        (let [buffer-queue (time (byte-buffer-queue num-images))]
          (println filename)
          (dotimes-timed [i num-images] 100
            (let [buffer (.take buffer-queue)]
              (write-buf channel buffer)
              )))
        (catch Exception e (println e))
        (finally (.close file)))
      filename))

(defn acquire-and-map
  [num-images]
  (let [buffer-queue (time (byte-buffer-queue num-images))]
    (let [filename (str "D:/AcquisitionData/test" (rand-int 100000) ".dat")
          file (RandomAccessFile. filename "rw")
          channel (.getChannel file)]
      (try
        (dotimes-timed [i num-images] 100
                       (let [pixels (.take buffer-queue)
                             out (.. file getChannel (map FileChannel$MapMode/READ_WRITE 0 (.limit pixels)))]
                         (.put out pixels)))
        (catch Exception e (println e))
        (finally (.close file))))))
        

(defn acquire-and-store-in-ram
  "Simulate running a sequence acquisition, and store
   the images in RAM using the direct byte buffer method."
  [num-images]
  (let [buffer-queue (time (byte-buffer-queue num-images))
        storage-queue (proxy [LinkedBlockingQueue] []
                        (toString [] (str "(" (count this) " items)")))]
      (dotimes-timed [i num-images] 100
        (let [buffer (.take buffer-queue)]
          (.put storage-queue buffer)))
    storage-queue))

(defn test-write [num-images]
  (let [buffer (byte-buffer (* 2560 2160 2))

        (.write channel buffer))
      (catch Exception e (println e))
      (finally
        (.close file)))))  

(defn monitor-circular-buffer []
  (while (not (core isSequenceRunning))
