(ns cljam.io.bam-index.reader
  (:require [clojure.java.io :as cio]
            [cljam.io.util.lsb.io-stream :as lsb]
            [cljam.io.bam-index.common :refer [bai-magic]]
            [cljam.io.util.chunk :as chunk]
            [cljam.util :as util])
  (:import java.util.Arrays
           [java.io FileInputStream Closeable IOException]
           [cljam.io.util.chunk Chunk]))
(deftype BAIReader [url reader]
  Closeable
  (close [this]
    (.close ^Closeable (.reader this))))
(defn- skip-chunks!
  [rdr]
  (let [n-chunks (int (lsb/read-int rdr))]
    (loop [i 0]
      (when (< i n-chunks)
        (lsb/skip rdr 16)
        (recur (inc i))))))
(defn- skip-bin-index!
  [rdr]
  (let [n-bidx (int (lsb/read-int rdr))]
    (loop [i 0]
      (when (< i n-bidx)
        (lsb/skip rdr 4)
        (skip-chunks! rdr)
        (recur (inc i))))))
(defn- skip-linear-index!
  [rdr]
  (let [n-lidx (int (lsb/read-int rdr))]
    (loop [i 0]
      (when (< i n-lidx)
        (lsb/skip rdr 8)
        (recur (inc i))))))
(defn- skip-index!
  [rdr ^long n]
  (loop [i 0]
    (when (< i n)
      (skip-bin-index! rdr)
      (skip-linear-index! rdr)
      (recur (inc i)))))
(defn- read-chunks!
  [rdr]
  (let [n (int (lsb/read-int rdr))]
    (loop [i 0, chunks []]
      (if (< i n)
        (recur (inc i) (conj chunks (Chunk. (lsb/read-long rdr) (lsb/read-long rdr))))
        chunks))))
(defn- read-bin-index**!
  [rdr]
  (let [n (int (lsb/read-int rdr))]
    (loop [i 0, bidx []]
      (if (< i n)
        (let [bin (lsb/read-int rdr)
              chunks (read-chunks! rdr)]
          (recur (inc i) (conj bidx {:bin bin, :chunks chunks})))
        bidx))))
(defn- read-bin-index*!
  [rdr ^long ref-idx]
  (let [n-ref (int (lsb/read-int rdr))]
    (when (>= ref-idx n-ref)
      (throw (IndexOutOfBoundsException. "The reference index number is invalid")))
    (skip-index! rdr ref-idx)
    (read-bin-index**! rdr)))
(defn- read-linear-index**!
  [rdr]
  (let [n (int (lsb/read-int rdr))]
    (loop [i 0, lidx []]
      (if (< i n)
        (recur (inc i) (conj lidx (lsb/read-long rdr)))
        lidx))))
(defn- read-linear-index*!
  [rdr ^long ref-idx]
  (let [n-ref (int (lsb/read-int rdr))]
    (when (>= ref-idx n-ref)
      (throw (IndexOutOfBoundsException. "The reference index number is invalid")))
    (skip-index! rdr ref-idx)
    (skip-bin-index! rdr)
    (read-linear-index**! rdr)))

Reads a binning index eagerly for the given reference index. Returns a vector of maps containing :bin and :chunks.

(defn read-bin-index!
  [^BAIReader rdr ref-idx]
  (read-bin-index*! (.reader rdr) ref-idx))

Reads a linear index eagerly for the given reference index. Returns a vector of linear index numbers

(defn read-linear-index!
  [^BAIReader rdr ref-idx]
  (read-linear-index*! (.reader rdr) ref-idx))

Reads all linear indices and bin indices. Returns a vector of maps containing :bidx and :lidx.

(defn read-all-index!
  [^BAIReader r]
  (let [rdr (.reader r)
        n-ref (lsb/read-int rdr)
        refs (range n-ref)
        all-idx (map (fn [_] [(read-bin-index**! rdr) (read-linear-index**! rdr)]) refs)
        bidx-seq (map first all-idx)
        bidx (zipmap
              refs
              (map (fn [bins]
                     (zipmap (map :bin bins) (map :chunks bins)))
                   bidx-seq))
        lidx (zipmap refs (map second all-idx))]
    {:bidx bidx
     :lidx lidx}))

Returns an open cljam.io.bam_index.reader.BAIReader of f. Should be used inside with-open to ensure the reader is properly closed.

(defn reader
  [f]
  (let [url (util/as-url f)
        r (if (= (.getProtocol url) "file")
            (FileInputStream. (cio/file url))
            (.openStream url))]
    (when-not (Arrays/equals ^bytes (lsb/read-bytes r 4) (.getBytes ^String bai-magic))
      (throw (IOException. "Invalid BAI file")))
    (->BAIReader url r)))