 (declare build-ssl-options select-compression)
 
 (def ^:dynamic *fetch-size*)
+(def ^:dynamic *async* false)
+
+;;
+;; Macros
+;;
+
+(defmacro async
+  "Prepare a single statement, return prepared statement"
+  [body]
+  `(binding [*async* true]
+     (do ~body)))
+
+(defmacro with-fetch-size
+  "Temporarily alters fetch size."
+  [^Integer n# & body]
+  `(binding [*fetch-size* ~n#]
+     (do ~@body)))
+
+(defmacro prepare
+  "Prepare a single statement, return prepared statement"
+  ([body]
+     `(binding [hayt/*prepared-statement* true
+                hayt/*param-stack*        (atom [])]
+        ~body))
+  ([session body]
+     `(binding [hayt/*prepared-statement* true
+                hayt/*param-stack*        (atom [])]
+        (.prepare ~session ~body))))
+
+;;
+;; Protocols
+;;
 
 (defprotocol DummySession
   (executeAsync [_ query]))

     (set-statement-options- (SimpleStatement. string-query))))

(defn bind
  [^PreparedStatement statement values]
  (.bind statement (to-array values)))

(defmacro prepare
  "Prepare a single statement, return prepared statement"
  ([body]
     `(binding [hayt/*prepared-statement* true
                hayt/*param-stack*        (atom [])]
        ~body))
  ([session body]
     `(binding [hayt/*prepared-statement* true
                hayt/*param-stack*        (atom [])]
        (.prepare ~session ~body))))

(defprotocol BuildStatement
  (build-statement [query]))

(extend-protocol BuildStatement
  String
  (build-statement [query]
    (build-statement (SimpleStatement. query)))

  clojure.lang.IPersistentMap
  (build-statement [raw-statement]
    (build-statement (hayt/->raw raw-statement)))

  Statement
  (build-statement [s]
    s))

(defprotocol Listenable
  (add-listener [_ runnable executor]))

(deftype AsyncResult [fut]
  clojure.lang.IDeref
  (deref [_]
    (conv/to-clj @fut))

  clojure.lang.IBlockingDeref
  (deref [_ time-period time-unit]
    (conv/to-clj (deref fut time-period time-unit)))

  Listenable
  (add-listener [_ runnable executor]
    (.addListener fut runnable executor)))

(defn execute
  [^Session session query]
  (if hayt/*prepared-statement*
    (let [^String q (hayt/->raw query)]
      (.prepare session q))
    (let [^Statement built-statement (build-statement query)]
      (if *async*
        (AsyncResult. (.executeAsync session built-statement))
        (-> (.execute session built-statement)
            (conv/to-clj))))))

(defn ^String export-schema
  "Exports the schema as a string"

            SSLOptions ProtocolOptions$Compression]
           [com.datastax.driver.auth DseAuthProvider]
           [com.google.common.util.concurrent ListenableFuture Futures FutureCallback]
           [java.net URI]
           [javax.net.ssl TrustManagerFactory KeyManagerFactory SSLContext]
           [java.security KeyStore SecureRandom]
           [com.datastax.driver.core.exceptions DriverException]))
